jsdw_ios/QuickLocation/Section/Mine/MoodStatus/MoodStatusView.swift

239 lines
8.0 KiB
Swift

//
// MoodStatusView.swift
// QuickLocation
//
import UIKit
import Lottie
final class MoodStatusView: UIView {
var onBack: (() -> Void)?
var onSave: (() -> Void)?
var onSelectMood: ((Int) -> Void)?
private let previewImageView = UIImageView(image: UIImage(named: "Mine/mood_preview_bg"))
private let previewLottieView: LottieAnimationView = {
let view = LottieAnimationView()
view.contentMode = .scaleAspectFit
view.loopMode = .loop
view.backgroundBehavior = .pauseAndRestore
return view
}()
lazy var navView = BaseNavigationView(title: "")
private let gridCard = UIView()
private let collectionLayout = CollectionHFlowLayout()
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionLayout)
private let pageControl = UIPageControl()
let saveBtn = UIButton(type: .custom)
private var items: [MoodStatusItem] = []
private var selectedMoodIndex: Int?
private var lastLayoutWidth: CGFloat = 0
private var previewAnimationName: String?
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setupUI()
updateGridLayout(for: bounds.width)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
updateGridLayout(for: bounds.width)
updatePreviewLottieFrame()
}
func configure(items: [MoodStatusItem], selectedMoodIndex: Int?) {
self.items = items
self.selectedMoodIndex = selectedMoodIndex
collectionView.reloadData()
updatePreview(with: selectedMoodIndex)
}
func updateSelection(moodIndex: Int?) {
selectedMoodIndex = moodIndex
collectionView.reloadData()
updatePreview(with: moodIndex)
}
func setSaving(_ saving: Bool) {
saveBtn.isEnabled = !saving
saveBtn.alpha = saving ? 0.65 : 1
collectionView.isUserInteractionEnabled = !saving
}
private func setupUI() {
addSubview(navView)
navView.layoutChain.edges(excludingEdge: .bottom).height(kNaviHeight)
previewImageView.contentMode = .scaleAspectFit
previewImageView.clipsToBounds = true
addSubview(previewImageView)
previewImageView.layoutChain
.topToBottomOfView(navView, offset: 8)
.edgesHorzontal()
.heightToWidth(264.0 / 375.0)
previewImageView.addSubview(previewLottieView)
saveBtn.setTitle("保存", for: .normal)
saveBtn.setTitleColor(.white, for: .normal)
saveBtn.titleLabel?.font = FontManager.boboBold(18)
saveBtn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
saveBtn.layer.cornerRadius = 20
saveBtn.clipsToBounds = true
saveBtn.addTarget(self, action: #selector(handleSave), for: .touchUpInside)
addSubview(saveBtn)
saveBtn.layoutChain
.edgesHorzontal(30)
.bottom(kSafeBottomMargin + 13)
.height(56)
gridCard.backgroundColor = UIColor(hexStr: "#F5F6F8")
gridCard.layer.cornerRadius = 40
addSubview(gridCard)
gridCard.layoutChain
.topToBottomOfView(previewImageView, offset: 25)
.edgesHorzontal(15)
.height(273)
collectionLayout.rows = 3
collectionLayout.colums = 4
collectionView.backgroundColor = .clear
collectionView.isPagingEnabled = true
collectionView.bounces = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(MoodStatusCell.self)
gridCard.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
pageControl.numberOfPages = 3
pageControl.currentPage = 0
pageControl.isUserInteractionEnabled = false
pageControl.currentPageIndicatorTintColor = UIColor(hexStr: "#16B3FF")
pageControl.pageIndicatorTintColor = UIColor(hexStr: "#7AD6FF", alpha: 0.4)
gridCard.addSubview(pageControl)
pageControl.layoutChain
.centerX()
.bottom(8)
.height(16)
collectionView.layoutChain
.edges(excludingEdge: .bottom)
.bottomToTopOfView(pageControl)
}
private func updateGridLayout(for width: CGFloat) {
let cardWidth = max(width - 32, 0)
guard cardWidth > 0, abs(cardWidth - lastLayoutWidth) > 0.5 else { return }
lastLayoutWidth = cardWidth
collectionLayout.itemSize = CGSize(width: 60, height: 60)
collectionLayout.hSpacing = 20
collectionLayout.vSpacing = 10
collectionLayout.sectionInset = UIEdgeInsets(top: 23, left: 23, bottom: 0, right: 23)
collectionLayout.invalidateLayout()
}
private func updatePreviewLottieFrame() {
let bounds = previewImageView.bounds
guard bounds.width > 0, bounds.height > 0 else { return }
previewLottieView.frame = CGRect(
x: (bounds.width - 80) / 2,
y: 30,
width: 80,
height: 80
)
}
private func updatePreview(with moodIndex: Int?) {
guard let moodIndex,
let item = MoodStatusCatalog.item(moodIndex: moodIndex),
let path = item.jsonPath else {
previewAnimationName = nil
previewLottieView.stop()
previewLottieView.animation = nil
return
}
if previewAnimationName == item.jsonName, previewLottieView.animation != nil {
if !previewLottieView.isAnimationPlaying {
previewLottieView.play()
}
return
}
previewAnimationName = item.jsonName
previewLottieView.stop()
previewLottieView.animation = LottieAnimation.filepath(path)
previewLottieView.currentProgress = 0
previewLottieView.play()
}
@objc private func handleBack() {
onBack?()
}
@objc private func handleSave() {
onSave?()
}
}
extension MoodStatusView: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: MoodStatusCell = collectionView.dequeueReusableCell(for: indexPath)
let item = items[indexPath.item]
cell.configure(image: item.pngImage, selected: item.moodIndex == selectedMoodIndex)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
onSelectMood?(items[indexPath.item].moodIndex)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
guard scrollView.bounds.width > 0 else { return }
pageControl.currentPage = Int(round(scrollView.contentOffset.x / scrollView.bounds.width))
}
}
final class MoodStatusCell: UICollectionViewCell {
private let imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 16
contentView.layer.borderWidth = 2
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
contentView.addSubview(imageView)
imageView.layoutChain.edges(all: 5)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(image: UIImage?, selected: Bool) {
imageView.image = image
contentView.layer.borderColor = selected ? UIColor(hexStr: "#16B3FF").cgColor : UIColor.clear.cgColor
contentView.backgroundColor = selected ? UIColor(hexStr: "#F3FBFF") : .white
}
}