Skip to content

Instantly share code, notes, and snippets.

@rnkyr
Created June 20, 2021 07:47
Show Gist options
  • Select an option

  • Save rnkyr/0ab12cdd151058caf11650677cc4287b to your computer and use it in GitHub Desktop.

Select an option

Save rnkyr/0ab12cdd151058caf11650677cc4287b to your computer and use it in GitHub Desktop.
import UIKit
public final class ChipsContainerView: NiblessView {
enum Style {
case withIcon, titleOnly
}
public var title: String {
get { titleLabel.text ?? "" }
set { titleLabel.text = newValue }
}
fileprivate var style: Style = .withIcon
fileprivate let titleLabel: UILabel = Factory.label()
.font(size: 14, style: .bold)
.textColor(Asset.Colors.gray2)
.build()
private var chipsViews: [ChipsView] = []
private var chipsWaitingForLayout: [String] = []
init() {
super.init(frame: .zero)
layout()
preservesSuperviewLayoutMargins = true
}
private func layout() {
addSubview(titleLabel)
titleLabel.layout {
$0.leading.equal(to: layoutMarginsGuide.leadingAnchor)
$0.trailing.equal(to: layoutMarginsGuide.trailingAnchor)
$0.top.equal(to: topAnchor, offsetBy: 16)
}
}
override public func layoutSubviews() {
super.layoutSubviews()
if !chipsWaitingForLayout.isEmpty {
setup(with: chipsWaitingForLayout)
}
}
public func setup(with chips: [String]) {
if bounds.width == 0 {
chipsWaitingForLayout = chips
return
}
chipsViews.forEach { $0.removeFromSuperview() }
chipsWaitingForLayout = []
let insets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
let interItemSpacing = CGPoint(x: 12, y: 12)
var lastView: UIView?
var verticalView: ChipsView?
var horizontalView: ChipsView?
var rowSize: CGFloat = insets.left
chipsViews = chips.map { title in
let view = ChipsView(.titleOnly)
view.titleLabel.text = title
view.translatesAutoresizingMaskIntoConstraints = false
view.layoutIfNeeded()
let fitsInARow = (rowSize + view.frame.width + insets.right) <= bounds.width
let singleRow: Bool
if !fitsInARow {
rowSize = insets.left
}
rowSize += view.frame.width + interItemSpacing.x
if fitsInARow {
singleRow = false
} else {
verticalView = horizontalView
horizontalView = nil
singleRow = (view.frame.width + insets.right + insets.left) > bounds.width
}
addSubview(view)
view.layout {
$0.top.equal(to: (verticalView ?? titleLabel).bottomAnchor, offsetBy: interItemSpacing.y)
$0.leading.equal(to: horizontalView?.trailingAnchor ?? leadingAnchor, offsetBy: interItemSpacing.x)
if singleRow {
$0.trailing.equal(to: trailingAnchor, offsetBy: -insets.right)
}
}
lastView = view
horizontalView = view
return view
}
lastView?.layout {
$0.bottom.equal(to: bottomAnchor, offsetBy: -insets.bottom)
}
}
}
extension ViewFactory where T: ChipsContainerView {
func kind(_ style: ChipsContainerView.Style) -> Self {
base.style = style
return self
}
}
final class ChipsView: NiblessView {
let titleLabel: UILabel = Factory.label()
.textColor(Asset.Colors.gray2)
.font(size: 14, style: .bold)
.numberOfLines(1)
.lineBreakMode(.byTruncatingTail)
.build()
let iconImageView: UIImageView = Factory.imageView()
.tintColor(Asset.Colors.gray2)
.contentMode(.scaleToFill)
.build()
init(_ style: ChipsContainerView.Style) {
super.init(frame: .zero)
layout(style)
self.style()
}
private func layout(_ style: ChipsContainerView.Style) {
switch style {
case .titleOnly:
titleLabel.layout(in: self, with: UIEdgeInsets(top: 8, left: 12, bottom: 8, right: 12))
case .withIcon:
let containerView = Factory.view().backgroundColor(Asset.Colors.transparent).build()
addSubviews(containerView, titleLabel)
containerView.layout {
$0.top.equal(to: topAnchor)
$0.bottom.equal(to: bottomAnchor)
$0.leading.equal(to: leadingAnchor)
$0.width.equal(to: 44)
}
titleLabel.layout {
$0.leading.equal(to: iconImageView.trailingAnchor)
$0.top.equal(to: topAnchor, offsetBy: 8)
$0.bottom.equal(to: bottomAnchor, offsetBy: -8)
$0.trailing.equal(to: trailingAnchor, offsetBy: -12)
}
iconImageView.layout {
$0.centerX.equal(to: containerView.centerXAnchor)
$0.centerY.equal(to: containerView.centerYAnchor)
}
layout {
$0.height.equal(to: 36)
}
}
}
private func style() {
backgroundColor = Asset.Colors.gray6.color
layer.cornerRadius = 18
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment