TM-SGNL-iOS/SignalUI/ViewControllers/HeroSheetViewController.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

132 lines
4.5 KiB
Swift

//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import Lottie
public class HeroSheetViewController: StackSheetViewController {
private enum Hero {
case image(UIImage)
case animation(named: String, height: CGFloat)
}
private let hero: Hero
private let titleText: String
private let bodyText: String
private let buttonTitle: String
private let buttonAction: (() -> Void)?
/// Creates a hero image sheet with a CTA button.
/// - Parameters:
/// - heroImage: Scaled image to display at the top of the sheet
/// - title: Localized title text
/// - body: Localized body text
/// - buttonTitle: Title for the CTA button
/// - didTapButton: Action for the CTA button.
/// If `nil`, the button will dismiss the sheet.
public init(
heroImage: UIImage,
title: String,
body: String,
buttonTitle: String,
didTapButton: (() -> Void)? = nil
) {
self.hero = .image(heroImage)
self.titleText = title
self.bodyText = body
self.buttonTitle = buttonTitle
self.buttonAction = didTapButton
super.init()
}
/// Creates a hero image sheet with a CTA button.
/// - Parameters:
/// - heroLottieName: Lottie name to display at the top of the sheet
/// - heroAnimationHeight: Height for the animation view
/// - title: Localized title text
/// - body: Localized body text
/// - buttonTitle: Title for the CTA button
/// - didTapButton: Action for the CTA button.
/// If `nil`, the button will dismiss the sheet.
public init(
heroAnimationName: String,
heroAnimationHeight: CGFloat,
title: String,
body: String,
buttonTitle: String,
didTapButton: (() -> Void)? = nil
) {
self.hero = .animation(named: heroAnimationName, height: heroAnimationHeight)
self.titleText = title
self.bodyText = body
self.buttonTitle = buttonTitle
self.buttonAction = didTapButton
super.init()
}
public override var stackViewInsets: UIEdgeInsets {
.init(top: 8, leading: 24, bottom: 32, trailing: 24)
}
public override func viewDidLoad() {
super.viewDidLoad()
let heroView: UIView
switch hero {
case let .image(image):
heroView = UIImageView(image: image)
heroView.contentMode = .center
case let .animation(lottieName, height):
let lottieView = LottieAnimationView(name: lottieName)
lottieView.autoSetDimension(.height, toSize: height)
lottieView.contentMode = .scaleAspectFit
lottieView.loopMode = .loop
lottieView.play()
heroView = lottieView
}
self.stackView.addArrangedSubview(heroView)
self.stackView.setCustomSpacing(20, after: heroView)
let titleLabel = UILabel()
self.stackView.addArrangedSubview(titleLabel)
self.stackView.setCustomSpacing(8, after: titleLabel)
titleLabel.text = self.titleText
titleLabel.font = .dynamicTypeTitle2.bold()
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
let bodyLabel = UILabel()
self.stackView.addArrangedSubview(bodyLabel)
self.stackView.setCustomSpacing(32, after: bodyLabel)
bodyLabel.text = self.bodyText
bodyLabel.font = .dynamicTypeSubheadline
bodyLabel.textColor = UIColor.Signal.secondaryLabel
bodyLabel.numberOfLines = 0
bodyLabel.textAlignment = .center
let button = UIButton(type: .system, primaryAction: UIAction { [weak self] _ in
if let self, let buttonAction {
buttonAction()
} else {
self?.dismiss(animated: true)
}
})
self.stackView.addArrangedSubview(button)
var buttonConfiguration = UIButton.Configuration.filled()
var buttonTitleAttributes = AttributeContainer()
buttonTitleAttributes.font = .dynamicTypeHeadline
buttonTitleAttributes.foregroundColor = .white
buttonConfiguration.attributedTitle = AttributedString(
self.buttonTitle,
attributes: buttonTitleAttributes
)
buttonConfiguration.contentInsets = .init(hMargin: 16, vMargin: 14)
buttonConfiguration.background.cornerRadius = 10
buttonConfiguration.background.backgroundColor = UIColor.Signal.ultramarine
button.configuration = buttonConfiguration
}
}