chromium/ios/chrome/common/ui/promo_style/promo_style_background_view.mm

// Copyright 2023 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/common/ui/promo_style/promo_style_background_view.h"

#import "base/ios/ios_util.h"
#import "ios/chrome/common/ui/util/image_util.h"
#import "ios/chrome/common/ui/util/ui_util.h"

namespace {

// Horizontal ratio of the left and right images in compact mode.
const CGFloat kLeftImageRatioCompact = 0.55;
const CGFloat kRightImageRatioCompact = 0.40;

// Horizontal ratio of the left and right images in regular mode.
const CGFloat kLeftImageRatioRegular = 0.35;
const CGFloat kRightImageRatioRegular = 0.25;

/// Whether the view is considered as compact.
BOOL IsCompact(UITraitCollection* traitCollection) {
  return traitCollection.horizontalSizeClass ==
             UIUserInterfaceSizeClassCompact &&
         traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact;
}

}  // namespace

@implementation PromoStyleBackgroundView {
  UIImageView* _leftImageView;
  UIImageView* _rightImageView;
}

- (instancetype)init {
  self = [super init];
  if (self) {
    _leftImageView = [[UIImageView alloc] init];
    _leftImageView.translatesAutoresizingMaskIntoConstraints = NO;
    _leftImageView.contentMode = UIViewContentModeTopLeft;
    [self addSubview:_leftImageView];

    _rightImageView = [[UIImageView alloc] init];
    _rightImageView.translatesAutoresizingMaskIntoConstraints = NO;
    _rightImageView.contentMode = UIViewContentModeTopRight;
    [self addSubview:_rightImageView];

    // The left and right images are RTL invariant. They are constrained to full
    // width here, the images are resized in `updateImages`.
    [NSLayoutConstraint activateConstraints:@[
      [_leftImageView.topAnchor constraintEqualToAnchor:self.topAnchor],
      [_leftImageView.rightAnchor constraintEqualToAnchor:self.rightAnchor],
      [_leftImageView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
      [_leftImageView.leftAnchor constraintEqualToAnchor:self.leftAnchor],

      [_rightImageView.topAnchor constraintEqualToAnchor:self.topAnchor],
      [_rightImageView.rightAnchor constraintEqualToAnchor:self.rightAnchor],
      [_rightImageView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
      [_rightImageView.leftAnchor constraintEqualToAnchor:self.leftAnchor],
    ]];

    if (@available(iOS 17, *)) {
      [self registerForTraitChanges:@[
        UITraitVerticalSizeClass.self, UITraitHorizontalSizeClass.self
      ]
                         withAction:@selector(updateImagesOnNextFrame)];
    }
  }
  return self;
}

- (void)layoutSubviews {
  [super layoutSubviews];

  // Initial layout.
  if (!_leftImageView.image) {
    [self updateImages];
  }
}

#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
  [super traitCollectionDidChange:previousTraitCollection];
  if (@available(iOS 17, *)) {
    return;
  }

  [self updateImagesOnNextFrame];
}
#endif

#pragma mark - Private

/// Updates images on the next frame to get the correct bounds.
- (void)updateImagesOnNextFrame {
  __weak __typeof__(self) weakSelf = self;
  dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf updateImages];
  });
}

/// Returns a resized `image` with a width covering the `horizontalRatio` of the
/// view. The height is computed to keep the same aspect ratio as the original
/// image.
- (UIImage*)resizeImage:(UIImage*)image
        horizontalRatio:(CGFloat)horizontalRatio {
  CGFloat imageAspectRatio = image.size.height / image.size.width;
  const CGFloat width =
      AlignValueToPixel(self.bounds.size.width * horizontalRatio);
  const CGFloat height =
      MIN(self.bounds.size.height, AlignValueToPixel(width * imageAspectRatio));

  return ResizeImage(image, CGSizeMake(width, height),
                     ProjectionMode::kAspectFillAlignTop);
}

/// Updates left and right images.
- (void)updateImages {
  // TODO(crbug.com/40944102): Add dark mode assets.
  BOOL isCompact = IsCompact(self.traitCollection);

  UIImage* leftImage = [UIImage imageNamed:@"promo_background_left"];
  CGFloat leftImageHorizontalRatio =
      isCompact ? kLeftImageRatioCompact : kLeftImageRatioRegular;
  _leftImageView.image = [self resizeImage:leftImage
                           horizontalRatio:leftImageHorizontalRatio];

  UIImage* rightImage = [UIImage imageNamed:@"promo_background_right"];
  CGFloat rightImageHorizontalRatio =
      isCompact ? kRightImageRatioCompact : kRightImageRatioRegular;
  _rightImageView.image = [self resizeImage:rightImage
                            horizontalRatio:rightImageHorizontalRatio];
}

@end