169 lines
4.9 KiB
Swift
169 lines
4.9 KiB
Swift
|
|
//
|
||
|
|
// PlaceholderView.swift
|
||
|
|
// Crush
|
||
|
|
//
|
||
|
|
// Created by Leon on 2025/7/14.
|
||
|
|
//
|
||
|
|
|
||
|
|
import SnapKit
|
||
|
|
import UIKit
|
||
|
|
|
||
|
|
/// A reusable placeholder view with an image and text label
|
||
|
|
class PlaceholderView: UIView {
|
||
|
|
// MARK: - Properties
|
||
|
|
|
||
|
|
lazy var emptyImage: UIImage? = {
|
||
|
|
UIImage(named: "empty_placeholder_icon")
|
||
|
|
}()
|
||
|
|
|
||
|
|
var viewStartOffset: CGFloat = 0
|
||
|
|
var startY: CGFloat = 0
|
||
|
|
|
||
|
|
private let placeholderImageView: UIImageView = {
|
||
|
|
let imageView = UIImageView()
|
||
|
|
imageView.contentMode = .scaleAspectFit
|
||
|
|
imageView.tintColor = .gray // Adjust tint for template images
|
||
|
|
return imageView
|
||
|
|
}()
|
||
|
|
|
||
|
|
private let placeholderLabel: UILabel = {
|
||
|
|
let label = UILabel()
|
||
|
|
label.textAlignment = .center
|
||
|
|
label.textColor = .gray
|
||
|
|
label.font = .systemFont(ofSize: 16, weight: .regular)
|
||
|
|
label.numberOfLines = 0 // Allow multiple lines
|
||
|
|
return label
|
||
|
|
}()
|
||
|
|
|
||
|
|
// MARK: - Initialization
|
||
|
|
|
||
|
|
init(text: String, viewStartOffset: CGFloat = 0) {
|
||
|
|
super.init(frame: .zero)
|
||
|
|
self.viewStartOffset = viewStartOffset
|
||
|
|
setupViews(image: emptyImage)
|
||
|
|
updateViewOffset(text: text)
|
||
|
|
}
|
||
|
|
|
||
|
|
init(text: String, startY: CGFloat = 0) {
|
||
|
|
super.init(frame: .zero)
|
||
|
|
self.startY = startY
|
||
|
|
setupViews(image: emptyImage)
|
||
|
|
updateViewStartY(text: text)
|
||
|
|
}
|
||
|
|
|
||
|
|
required init?(coder: NSCoder) {
|
||
|
|
fatalError("init(coder:) has not been implemented")
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Setup
|
||
|
|
|
||
|
|
private func setupViews(image: UIImage?) {
|
||
|
|
isUserInteractionEnabled = false
|
||
|
|
backgroundColor = .clear // Adjust as needed
|
||
|
|
|
||
|
|
// Add subviews
|
||
|
|
addSubview(placeholderImageView)
|
||
|
|
addSubview(placeholderLabel)
|
||
|
|
|
||
|
|
// Configure content
|
||
|
|
placeholderImageView.image = image
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Public Methods
|
||
|
|
|
||
|
|
func updateViewOffset(text: String) {
|
||
|
|
placeholderLabel.text = text
|
||
|
|
placeholderImageView.snp.makeConstraints { make in
|
||
|
|
make.centerX.equalToSuperview()
|
||
|
|
make.centerY.equalToSuperview().offset((-40 + viewStartOffset) * 0.5) // Offset to position image above text
|
||
|
|
make.size.equalTo(CGSize(width: 183, height: 135))
|
||
|
|
}
|
||
|
|
|
||
|
|
placeholderLabel.snp.makeConstraints { make in
|
||
|
|
make.top.equalTo(placeholderImageView.snp.bottom).offset(16)
|
||
|
|
make.left.right.equalToSuperview().inset(16)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func updateViewStartY(text: String) {
|
||
|
|
placeholderLabel.text = text
|
||
|
|
placeholderImageView.snp.makeConstraints { make in
|
||
|
|
make.centerX.equalToSuperview()
|
||
|
|
make.top.equalToSuperview().offset(startY)
|
||
|
|
make.width.height.equalTo(100) // Adjust size as needed
|
||
|
|
}
|
||
|
|
|
||
|
|
placeholderLabel.snp.makeConstraints { make in
|
||
|
|
make.top.equalTo(placeholderImageView.snp.bottom).offset(16)
|
||
|
|
make.left.right.equalToSuperview().inset(16)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - UIView Extension
|
||
|
|
|
||
|
|
extension UIView {
|
||
|
|
/// Adds a placeholder view with an image and text to the view
|
||
|
|
/// - Parameters:
|
||
|
|
/// - text: The placeholder text
|
||
|
|
/// - Returns: The created PlaceholderView instance
|
||
|
|
@discardableResult
|
||
|
|
func showEmpty(text: String, viewStartOffset: CGFloat = 0) -> PlaceholderView {
|
||
|
|
// Remove any existing placeholder views
|
||
|
|
subviews.filter { $0 is PlaceholderView }.forEach { $0.removeFromSuperview() }
|
||
|
|
|
||
|
|
// Create and configure placeholder view
|
||
|
|
let placeholderView = PlaceholderView(text: text, viewStartOffset: viewStartOffset)
|
||
|
|
addSubview(placeholderView)
|
||
|
|
|
||
|
|
// Setup Auto Layout
|
||
|
|
placeholderView.snp.makeConstraints { make in
|
||
|
|
make.edges.equalToSuperview()
|
||
|
|
}
|
||
|
|
|
||
|
|
placeholderView.setNeedsDisplay()
|
||
|
|
placeholderView.layoutIfNeeded()
|
||
|
|
|
||
|
|
return placeholderView
|
||
|
|
}
|
||
|
|
|
||
|
|
@discardableResult
|
||
|
|
func showStartYEmpty(text: String, startY: CGFloat = 200) -> PlaceholderView {
|
||
|
|
// Remove any existing placeholder views
|
||
|
|
subviews.filter { $0 is PlaceholderView }.forEach { $0.removeFromSuperview() }
|
||
|
|
|
||
|
|
// Create and configure placeholder view
|
||
|
|
let placeholderView = PlaceholderView(text: text, startY: startY)
|
||
|
|
addSubview(placeholderView)
|
||
|
|
|
||
|
|
// Setup Auto Layout
|
||
|
|
placeholderView.snp.makeConstraints { make in
|
||
|
|
make.edges.equalToSuperview()
|
||
|
|
}
|
||
|
|
|
||
|
|
return placeholderView
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Removes any existing placeholder view
|
||
|
|
func removeEmpty() {
|
||
|
|
subviews.filter { $0 is PlaceholderView }.forEach { $0.removeFromSuperview() }
|
||
|
|
}
|
||
|
|
|
||
|
|
func hideEmpty(){
|
||
|
|
removeEmpty()
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Public
|
||
|
|
func setupEmpty(empty: Bool, startY: CGFloat? = 0, msg: String?){
|
||
|
|
if(empty){
|
||
|
|
if let y = startY, y > 0{
|
||
|
|
showStartYEmpty(text: msg ?? "", startY: y)
|
||
|
|
}else{
|
||
|
|
showEmpty(text: msg ?? "")
|
||
|
|
}
|
||
|
|
}else{
|
||
|
|
removeEmpty()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|