Skip to content

Instantly share code, notes, and snippets.

@dterekhov
Created November 13, 2025 09:09
Show Gist options
  • Select an option

  • Save dterekhov/d46857f86e3b095274a72a506e1c66f3 to your computer and use it in GitHub Desktop.

Select an option

Save dterekhov/d46857f86e3b095274a72a506e1c66f3 to your computer and use it in GitHub Desktop.
UITableView+EmptyData: Placehoder for UITableView on no data state #uikit #swift-api #real-project
import UIKit
extension UITableView {
// MARK: - Public API
public func fw_setPlaceholder
(text: String?,
fontSize: CGFloat = 21,
image: UIImage?,
edgeInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)) {
let stackView = fw_placeholderStackView()
if let text = text {
stackView.addArrangedSubview(fw_placeholderLabel(byText: text, fontSize: fontSize))
}
if let image = image {
stackView.addArrangedSubview(fw_placeholderImageView(byImage: image))
}
let isPlacehoderEnabled = !stackView.arrangedSubviews.isEmpty // At least one subview
if isPlacehoderEnabled {
let placeholderView = UIView(frame: frame)
placeholderView.backgroundColor = UIColor.green.withAlphaComponent(0.5)
placeholderView.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: placeholderView.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: placeholderView.centerYAnchor,
constant: -edgeInsets.top + edgeInsets.bottom).isActive = true
stackView.leadingAnchor.constraint(equalTo: placeholderView.leadingAnchor,
constant: edgeInsets.left).isActive = true
stackView.trailingAnchor.constraint(equalTo: placeholderView.trailingAnchor,
constant: -edgeInsets.right).isActive = true
backgroundView = placeholderView
separatorStyle = .none
} else {
backgroundView = nil
separatorStyle = .singleLine
}
}
// MARK: - Private API
private enum Constants {
static let color = #colorLiteral(red: 0.5607843137, green: 0.5803921569, blue: 0.6117647059, alpha: 1)
}
private func fw_placeholderStackView() -> UIStackView {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .center
stackView.distribution = .fill
stackView.spacing = 30
return stackView
}
private func fw_placeholderLabel(byText text: String, fontSize: CGFloat) -> UILabel {
let textAttributes: [NSAttributedString.Key: Any] =
[NSAttributedString.Key.foregroundColor: Constants.color,
NSAttributedString.Key.font: UIFont.systemFont(ofSize: fontSize)]
// Centering existed attributedString
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
let mutableMessage = NSMutableAttributedString(string: text, attributes: textAttributes)
mutableMessage.addAttribute(NSAttributedString.Key.paragraphStyle,
value: style,
range: (mutableMessage.string as NSString)
.range(of: mutableMessage.string))
let messageLabel = UILabel(frame: CGRect(x: 0,
y: 0,
width: bounds.size.width,
height: bounds.size.height))
messageLabel.attributedText = mutableMessage
messageLabel.numberOfLines = 0
messageLabel.sizeToFit()
return messageLabel
}
private func fw_placeholderImageView(byImage image: UIImage) -> UIImageView {
let templateImage = image.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: templateImage)
imageView.tintColor = Constants.color
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.widthAnchor.constraint(equalToConstant: image.size.width).isActive = true
imageView.heightAnchor.constraint(equalToConstant: image.size.height).isActive = true
return imageView
}
}
@dterekhov
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment