blob: cc2334752870cd02773d1236cc4040d0df33aa9c [file] [log] [blame] [edit]
// Copyright 2016-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 "MDCCollectionInfoBarView.h"
#import "MDCCollectionInfoBarViewDelegate.h"
#import "MaterialPalettes.h"
#import "MaterialShadowLayer.h"
#import "MaterialTypography.h"
const CGFloat MDCCollectionInfoBarAnimationDuration = (CGFloat)0.3;
const CGFloat MDCCollectionInfoBarHeaderHeight = 48;
const CGFloat MDCCollectionInfoBarFooterHeight = 48;
static const CGFloat MDCCollectionInfoBarLabelHorizontalPadding = 16;
static inline UIColor *CollectionInfoBarBlueColor(void) {
return MDCPalette.bluePalette.accent200;
}
static inline UIColor *CollectionInfoBarRedColor(void) {
return MDCPalette.redPalette.tint500;
}
@interface ShadowedView : UIView
@end
@implementation ShadowedView
+ (Class)layerClass {
return [MDCShadowLayer class];
}
@end
@implementation MDCCollectionInfoBarView {
CGFloat _backgroundTransformY;
CALayer *_backgroundBorderLayer;
UITapGestureRecognizer *_tapGesture;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonMDCCollectionInfoBarViewInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonMDCCollectionInfoBarViewInit];
}
return self;
}
- (void)commonMDCCollectionInfoBarViewInit {
self.backgroundColor = [UIColor clearColor];
self.userInteractionEnabled = NO;
_backgroundView = [[ShadowedView alloc] initWithFrame:self.bounds];
_backgroundView.autoresizingMask =
(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
_backgroundView.hidden = YES;
_titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.font = [MDCTypography body1Font];
_titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_backgroundView addSubview:_titleLabel];
[self addSubview:_backgroundView];
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleTapGesture:)];
[_backgroundView addGestureRecognizer:_tapGesture];
}
- (void)layoutSubviews {
[super layoutSubviews];
UIEdgeInsets collectionViewSafeAreaInsets = UIEdgeInsetsZero;
if (self.superview) {
collectionViewSafeAreaInsets = self.superview.safeAreaInsets;
}
CGFloat leftInset =
MAX(MDCCollectionInfoBarLabelHorizontalPadding, collectionViewSafeAreaInsets.left);
CGFloat rightInset =
MAX(MDCCollectionInfoBarLabelHorizontalPadding, collectionViewSafeAreaInsets.right);
CGFloat height = [_kind isEqualToString:MDCCollectionInfoBarKindHeader]
? MDCCollectionInfoBarHeaderHeight
: MDCCollectionInfoBarFooterHeight;
_titleLabel.frame =
CGRectMake(leftInset, 0, CGRectGetWidth(self.bounds) - (leftInset + rightInset), height);
if (_shouldApplyBackgroundViewShadow) {
[self setShouldApplyBackgroundViewShadow:_shouldApplyBackgroundViewShadow];
}
}
- (void)layoutSublayersOfLayer:(CALayer *)layer {
[super layoutSublayersOfLayer:layer];
// Set border layer frame.
_backgroundBorderLayer.frame = CGRectMake(-1, 0, CGRectGetWidth(self.backgroundView.bounds) + 2,
CGRectGetHeight(self.backgroundView.bounds) + 1);
}
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
[super applyLayoutAttributes:layoutAttributes];
self.layer.zPosition = layoutAttributes.zIndex;
}
- (void)setTintColor:(UIColor *)tintColor {
_tintColor = tintColor;
_backgroundView.backgroundColor = _tintColor;
}
- (void)setMessage:(NSString *)message {
_message = message;
_titleLabel.text = message;
}
- (void)setKind:(NSString *)kind {
_kind = kind;
_backgroundTransformY = CGRectGetHeight(self.bounds);
if ([kind isEqualToString:MDCCollectionInfoBarKindHeader]) {
_backgroundTransformY *= -1;
}
_backgroundView.transform = CGAffineTransformMakeTranslation(0, _backgroundTransformY);
}
- (void)setShouldApplyBackgroundViewShadow:(BOOL)shouldApplyBackgroundViewShadow {
_shouldApplyBackgroundViewShadow = shouldApplyBackgroundViewShadow;
MDCShadowLayer *shadowLayer = (MDCShadowLayer *)_backgroundView.layer;
shadowLayer.elevation = shouldApplyBackgroundViewShadow ? 1 : 0;
}
- (void)setTextAlignment:(NSTextAlignment)textAlignment {
_textAlignment = textAlignment;
_titleLabel.textAlignment = textAlignment;
}
- (void)setStyle:(MDCCollectionInfoBarViewStyle)style {
_style = style;
if (style == MDCCollectionInfoBarViewStyleHUD) {
self.allowsTap = NO;
self.shouldApplyBackgroundViewShadow = NO;
self.textAlignment = NSTextAlignmentLeft;
self.tintColor = CollectionInfoBarBlueColor();
self.titleLabel.textColor = [UIColor whiteColor];
self.autoDismissAfterDuration = 1;
self.backgroundView.alpha = (CGFloat)0.9;
} else if (style == MDCCollectionInfoBarViewStyleActionable) {
self.allowsTap = YES;
self.shouldApplyBackgroundViewShadow = YES;
self.textAlignment = NSTextAlignmentCenter;
self.tintColor = [UIColor whiteColor];
self.titleLabel.textColor = CollectionInfoBarRedColor();
self.autoDismissAfterDuration = 0;
self.backgroundView.alpha = 1;
self.isAccessibilityElement = YES;
self.accessibilityTraits = UIAccessibilityTraitButton;
self.accessibilityLabel = self.message;
// Adds border to be positioned during sublayer layout.
self.backgroundView.clipsToBounds = YES;
if (!_backgroundBorderLayer) {
_backgroundBorderLayer = [CALayer layer];
_backgroundBorderLayer.borderColor = [UIColor colorWithWhite:0 alpha:(CGFloat)0.1].CGColor;
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION
// For code review, use the review queue listed inĀ go/material-visionos-review.
UITraitCollection *current = [UITraitCollection currentTraitCollection];
CGFloat scale = current ? [current displayScale] : 1.0;
_backgroundBorderLayer.borderWidth = 1 / scale;
#else
_backgroundBorderLayer.borderWidth = 1 / [[UIScreen mainScreen] scale];
#endif
[self.backgroundView.layer addSublayer:_backgroundBorderLayer];
}
}
}
- (BOOL)isVisible {
return !_backgroundView.hidden;
}
- (void)showAnimated:(BOOL)animated {
[self layoutIfNeeded];
_backgroundView.hidden = NO;
// Notify delegate.
if ([_delegate respondsToSelector:@selector(infoBar:willShowAnimated:willAutoDismiss:)]) {
[_delegate infoBar:self willShowAnimated:animated willAutoDismiss:[self shouldAutoDismiss]];
}
NSTimeInterval duration = (animated) ? MDCCollectionInfoBarAnimationDuration : 0;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.backgroundView.transform = CGAffineTransformIdentity;
}
completion:^(__unused BOOL finished) {
self.userInteractionEnabled = self.allowsTap;
// Notify delegate.
if ([self.delegate respondsToSelector:@selector(infoBar:
didShowAnimated:willAutoDismiss:)]) {
[self.delegate infoBar:self
didShowAnimated:animated
willAutoDismiss:[self shouldAutoDismiss]];
}
[self autoDismissIfNecessaryWithAnimation:animated];
}];
}
- (void)dismissAnimated:(BOOL)animated {
// Notify delegate.
if ([_delegate respondsToSelector:@selector(infoBar:willDismissAnimated:willAutoDismiss:)]) {
[_delegate infoBar:self willDismissAnimated:animated willAutoDismiss:[self shouldAutoDismiss]];
}
NSTimeInterval duration = (animated) ? MDCCollectionInfoBarAnimationDuration : 0;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.backgroundView.transform =
CGAffineTransformMakeTranslation(0, self->_backgroundTransformY);
}
completion:^(__unused BOOL finished) {
self.userInteractionEnabled = NO;
self.backgroundView.hidden = YES;
// Notify delegate.
if ([self.delegate respondsToSelector:@selector(infoBar:
didDismissAnimated:didAutoDismiss:)]) {
[self.delegate infoBar:self
didDismissAnimated:animated
didAutoDismiss:[self shouldAutoDismiss]];
}
}];
}
#pragma mark - Private
- (void)handleTapGesture:(__unused UITapGestureRecognizer *)recognizer {
if ([_delegate respondsToSelector:@selector(didTapInfoBar:)]) {
[_delegate didTapInfoBar:self];
}
}
- (BOOL)shouldAutoDismiss {
return (_autoDismissAfterDuration > 0);
}
- (void)autoDismissIfNecessaryWithAnimation:(BOOL)animation {
if ([self shouldAutoDismiss]) {
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoDismissAfterDuration * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
[self dismissAnimated:animation];
});
}
}
@end