248 lines
8.4 KiB
Swift
248 lines
8.4 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 cardSideInset: CGFloat = 15
|
|
let cardWidth = gridCard.bounds.width > 1
|
|
? gridCard.bounds.width
|
|
: max(width - cardSideInset * 2, 0)
|
|
guard cardWidth > 0, abs(cardWidth - lastLayoutWidth) > 0.5 else { return }
|
|
lastLayoutWidth = cardWidth
|
|
|
|
let columns: CGFloat = 4
|
|
let itemWidth: CGFloat = 60
|
|
let hSpacing: CGFloat = 20
|
|
let rowWidth = columns * itemWidth + (columns - 1) * hSpacing
|
|
let sideInset = max((cardWidth - rowWidth) / 2, 0)
|
|
|
|
collectionLayout.itemSize = CGSize(width: itemWidth, height: 60)
|
|
collectionLayout.hSpacing = hSpacing
|
|
collectionLayout.vSpacing = 10
|
|
collectionLayout.sectionInset = UIEdgeInsets(top: 23, left: sideInset, bottom: 0, right: sideInset)
|
|
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
|
|
}
|
|
}
|