Files
Emoney-Info---IOS/Emoney Info/AboutViewController.swift
Wira Basalamah 8f0b001501 Initial commit
2026-04-24 04:55:24 +07:00

367 lines
17 KiB
Swift

// AboutViewController.swift
// Emoney Info
import UIKit
class AboutViewController: UIViewController {
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Theme.Color.background
setupUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
(tabBarController as? MainTabBarController)?.setTabBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
(tabBarController as? MainTabBarController)?.setTabBarHidden(false, animated: animated)
}
// MARK: - Setup
private func setupUI() {
let scrollView = UIScrollView()
let contentView = UIView()
scrollView.showsVerticalScrollIndicator = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
scrollView.addSubview(contentView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
])
// MARK: Back button + nav title
let backButton = UIButton(type: .system)
let chevron = UIImage(systemName: "chevron.left",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 16, weight: .semibold))
backButton.setImage(chevron, for: .normal)
backButton.tintColor = Theme.Color.textPrimary
backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside)
backButton.translatesAutoresizingMaskIntoConstraints = false
let navTitleLabel = UILabel()
navTitleLabel.text = L10n.aboutAppTitle
navTitleLabel.font = Theme.Font.caption(weight: .semibold)
navTitleLabel.textColor = Theme.Color.textSecondary
navTitleLabel.translatesAutoresizingMaskIntoConstraints = false
// MARK: App icon
let iconView = UIImageView(image: UIImage(named: "AppLogo"))
iconView.contentMode = .scaleAspectFit
iconView.layer.cornerRadius = 24
iconView.clipsToBounds = true
iconView.layer.shadowColor = UIColor.black.cgColor
iconView.layer.shadowOpacity = 0.12
iconView.layer.shadowOffset = CGSize(width: 0, height: 4)
iconView.layer.shadowRadius = 12
iconView.translatesAutoresizingMaskIntoConstraints = false
// MARK: App name + version
let appNameLabel = UILabel()
appNameLabel.text = "Emoney Info"
appNameLabel.font = .systemFont(ofSize: 28, weight: .bold)
appNameLabel.textColor = Theme.Color.textPrimary
appNameLabel.textAlignment = .center
appNameLabel.translatesAutoresizingMaskIntoConstraints = false
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
let versionLabel = UILabel()
let versionText = "VERSI \(appVersion)"
let versionAttr = NSAttributedString(string: versionText, attributes: [
.kern: 1.5,
.font: Theme.Font.caption(weight: .semibold),
.foregroundColor: Theme.Color.textSecondary,
])
versionLabel.attributedText = versionAttr
versionLabel.textAlignment = .center
versionLabel.translatesAutoresizingMaskIntoConstraints = false
// MARK: Description
let descLabel = UILabel()
descLabel.text = L10n.aboutAppDescription
descLabel.font = Theme.Font.body(weight: .regular)
descLabel.textColor = Theme.Color.textSecondary
descLabel.numberOfLines = 0
descLabel.textAlignment = .center
descLabel.translatesAutoresizingMaskIntoConstraints = false
// MARK: Feature chips
let chipsStack = UIStackView(arrangedSubviews: [
makeFeatureChip(L10n.aboutChipNfc),
makeFeatureChip(L10n.aboutChipRealtime),
makeFeatureChip(L10n.aboutChipMulti),
])
chipsStack.axis = .horizontal
chipsStack.spacing = 10
chipsStack.alignment = .center
chipsStack.translatesAutoresizingMaskIntoConstraints = false
// MARK: Legal rows card
let legalCard = makeCard()
let termsRow = makeLegalRow(icon: "doc.text", title: L10n.aboutTerms)
termsRow.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(termsTapped)))
termsRow.isUserInteractionEnabled = true
let separator = UIView()
separator.backgroundColor = Theme.Color.background
separator.translatesAutoresizingMaskIntoConstraints = false
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
let privacyRow = makeLegalRow(icon: "lock.shield", title: L10n.aboutPrivacy)
privacyRow.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(privacyTapped)))
privacyRow.isUserInteractionEnabled = true
let legalStack = UIStackView(arrangedSubviews: [termsRow, separator, privacyRow])
legalStack.axis = .vertical
legalStack.spacing = 0
legalStack.translatesAutoresizingMaskIntoConstraints = false
legalCard.addSubview(legalStack)
// MARK: Connect card (teal gradient)
let connectCard = UIView()
connectCard.layer.cornerRadius = 20
connectCard.clipsToBounds = true
connectCard.translatesAutoresizingMaskIntoConstraints = false
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [
Theme.Color.primary.cgColor,
Theme.Color.secondary.cgColor,
]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
connectCard.layer.insertSublayer(gradientLayer, at: 0)
let connectIcon = UIImageView(image: UIImage(systemName: "wave.3.right.circle.fill",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 32, weight: .medium)))
connectIcon.tintColor = UIColor.white.withAlphaComponent(0.35)
connectIcon.contentMode = .scaleAspectFit
connectIcon.translatesAutoresizingMaskIntoConstraints = false
let connectTitle = UILabel()
connectTitle.text = L10n.aboutConnectTitle
connectTitle.font = Theme.Font.subtitle(weight: .bold)
connectTitle.textColor = .white
connectTitle.numberOfLines = 0
connectTitle.translatesAutoresizingMaskIntoConstraints = false
let connectDesc = UILabel()
connectDesc.text = L10n.aboutConnectDesc
connectDesc.font = Theme.Font.caption(weight: .regular)
connectDesc.textColor = UIColor.white.withAlphaComponent(0.85)
connectDesc.numberOfLines = 0
connectDesc.translatesAutoresizingMaskIntoConstraints = false
[connectIcon, connectTitle, connectDesc].forEach { connectCard.addSubview($0) }
// MARK: Copyright
let copyrightLabel = UILabel()
copyrightLabel.text = L10n.footerCopyright
copyrightLabel.font = Theme.Font.caption(weight: .regular)
copyrightLabel.textColor = Theme.Color.textSecondary
copyrightLabel.textAlignment = .center
copyrightLabel.translatesAutoresizingMaskIntoConstraints = false
// MARK: Add to contentView
[backButton, navTitleLabel, iconView, appNameLabel, versionLabel,
descLabel, chipsStack, legalCard, connectCard, copyrightLabel]
.forEach { contentView.addSubview($0) }
NSLayoutConstraint.activate([
// Nav row
backButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 56),
backButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
backButton.widthAnchor.constraint(equalToConstant: 32),
backButton.heightAnchor.constraint(equalToConstant: 32),
navTitleLabel.centerYAnchor.constraint(equalTo: backButton.centerYAnchor),
navTitleLabel.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 8),
// Icon
iconView.topAnchor.constraint(equalTo: backButton.bottomAnchor, constant: 28),
iconView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
iconView.widthAnchor.constraint(equalToConstant: 96),
iconView.heightAnchor.constraint(equalToConstant: 96),
// Name + version
appNameLabel.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 16),
appNameLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
versionLabel.topAnchor.constraint(equalTo: appNameLabel.bottomAnchor, constant: 6),
versionLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// Desc
descLabel.topAnchor.constraint(equalTo: versionLabel.bottomAnchor, constant: 16),
descLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 32),
descLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -32),
// Chips
chipsStack.topAnchor.constraint(equalTo: descLabel.bottomAnchor, constant: 20),
chipsStack.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// Legal card
legalCard.topAnchor.constraint(equalTo: chipsStack.bottomAnchor, constant: 28),
legalCard.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
legalCard.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
legalStack.topAnchor.constraint(equalTo: legalCard.topAnchor, constant: 4),
legalStack.leadingAnchor.constraint(equalTo: legalCard.leadingAnchor),
legalStack.trailingAnchor.constraint(equalTo: legalCard.trailingAnchor),
legalStack.bottomAnchor.constraint(equalTo: legalCard.bottomAnchor, constant: -4),
// Connect card
connectCard.topAnchor.constraint(equalTo: legalCard.bottomAnchor, constant: 20),
connectCard.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
connectCard.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
connectIcon.topAnchor.constraint(equalTo: connectCard.topAnchor, constant: 20),
connectIcon.trailingAnchor.constraint(equalTo: connectCard.trailingAnchor, constant: -20),
connectIcon.widthAnchor.constraint(equalToConstant: 44),
connectIcon.heightAnchor.constraint(equalToConstant: 44),
connectTitle.topAnchor.constraint(equalTo: connectCard.topAnchor, constant: 24),
connectTitle.leadingAnchor.constraint(equalTo: connectCard.leadingAnchor, constant: 20),
connectTitle.trailingAnchor.constraint(equalTo: connectIcon.leadingAnchor, constant: -12),
connectDesc.topAnchor.constraint(equalTo: connectTitle.bottomAnchor, constant: 10),
connectDesc.leadingAnchor.constraint(equalTo: connectCard.leadingAnchor, constant: 20),
connectDesc.trailingAnchor.constraint(equalTo: connectCard.trailingAnchor, constant: -20),
connectDesc.bottomAnchor.constraint(equalTo: connectCard.bottomAnchor, constant: -24),
// Copyright
copyrightLabel.topAnchor.constraint(equalTo: connectCard.bottomAnchor, constant: 24),
copyrightLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
copyrightLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -32),
])
// Gradient frame set after layout
DispatchQueue.main.async {
gradientLayer.frame = connectCard.bounds
}
}
// MARK: - Helpers
private func makeCard() -> UIView {
let v = UIView()
v.backgroundColor = Theme.Color.card
v.layer.cornerRadius = 16
v.layer.shadowColor = UIColor.black.cgColor
v.layer.shadowOpacity = 0.06
v.layer.shadowOffset = CGSize(width: 0, height: 2)
v.layer.shadowRadius = 8
v.translatesAutoresizingMaskIntoConstraints = false
return v
}
private func makeFeatureChip(_ title: String) -> UIView {
let container = UIView()
container.backgroundColor = Theme.Color.primary.withAlphaComponent(0.12)
container.layer.cornerRadius = 12
container.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = title
label.font = Theme.Font.caption(weight: .semibold)
label.textColor = Theme.Color.secondary
label.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(label)
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: container.topAnchor, constant: 6),
label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 12),
label.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -12),
label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -6),
])
return container
}
private func makeLegalRow(icon: String, title: String) -> UIView {
let row = UIView()
row.translatesAutoresizingMaskIntoConstraints = false
let iconContainer = UIView()
iconContainer.backgroundColor = Theme.Color.background
iconContainer.layer.cornerRadius = 10
iconContainer.translatesAutoresizingMaskIntoConstraints = false
let iconView = UIImageView(image: UIImage(systemName: icon,
withConfiguration: UIImage.SymbolConfiguration(pointSize: 14, weight: .medium)))
iconView.tintColor = Theme.Color.secondary
iconView.contentMode = .scaleAspectFit
iconView.translatesAutoresizingMaskIntoConstraints = false
iconContainer.addSubview(iconView)
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = Theme.Font.body(weight: .medium)
titleLabel.textColor = Theme.Color.textPrimary
titleLabel.translatesAutoresizingMaskIntoConstraints = false
let chevronImg = UIImage(systemName: "chevron.right",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
let chevron = UIImageView(image: chevronImg)
chevron.tintColor = Theme.Color.textSecondary
chevron.translatesAutoresizingMaskIntoConstraints = false
[iconContainer, titleLabel, chevron].forEach { row.addSubview($0) }
NSLayoutConstraint.activate([
iconContainer.widthAnchor.constraint(equalToConstant: 36),
iconContainer.heightAnchor.constraint(equalToConstant: 36),
iconView.centerXAnchor.constraint(equalTo: iconContainer.centerXAnchor),
iconView.centerYAnchor.constraint(equalTo: iconContainer.centerYAnchor),
iconView.widthAnchor.constraint(equalToConstant: 18),
iconView.heightAnchor.constraint(equalToConstant: 18),
iconContainer.leadingAnchor.constraint(equalTo: row.leadingAnchor, constant: 16),
iconContainer.centerYAnchor.constraint(equalTo: row.centerYAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconContainer.trailingAnchor, constant: 14),
titleLabel.centerYAnchor.constraint(equalTo: row.centerYAnchor),
titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: chevron.leadingAnchor, constant: -8),
chevron.trailingAnchor.constraint(equalTo: row.trailingAnchor, constant: -16),
chevron.centerYAnchor.constraint(equalTo: row.centerYAnchor),
row.heightAnchor.constraint(equalToConstant: 60),
])
return row
}
// MARK: - Actions
@objc private func backTapped() {
navigationController?.popViewController(animated: true)
}
@objc private func termsTapped() {
let vc = TermsViewController()
navigationController?.pushViewController(vc, animated: true)
}
@objc private func privacyTapped() {
let vc = PrivacyPolicyViewController()
navigationController?.pushViewController(vc, animated: true)
}
}