Skip to content

Instantly share code, notes, and snippets.

@kyungpyoda
Created July 14, 2021 07:05
Show Gist options
  • Select an option

  • Save kyungpyoda/a4e576b0e4cab9400959040fc00f9df2 to your computer and use it in GitHub Desktop.

Select an option

Save kyungpyoda/a4e576b0e4cab9400959040fc00f9df2 to your computer and use it in GitHub Desktop.
[iOS] Foldable View
//
// FoldableView.swift
//
// Created by 홍경표 on 2021/07/14.
//
import UIKit
final class FoldableView: UIView {
var isFolded: Bool {
didSet {
let isFolded = isFolded
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: .curveEaseInOut) { [weak self] in
self?.containerView.isHidden = isFolded
self?.arrowLabel.transform = isFolded ? .identity : CGAffineTransform(rotationAngle: .pi/2)
}
}
}
private lazy var tapGesture = UITapGestureRecognizer(target: self, action: #selector(componentTapped))
/// ... 버튼 tap 됐을 때 불리는 handler
var dotsButtonHandler: (() -> Void)?
/// 각 subview가 tap 됐을 때 불리는 handler
var componentTappedHandler: ((UIView) -> Void)?
private var components: [UIView] {
didSet {
titleLabel.text = "[\(components.count)] Foldable"
}
}
private let containerView: UIStackView = .init().then {
$0.backgroundColor = .systemYellow
}
private let titleLabel: UILabel = .init()
private let arrowLabel: UILabel = .init().then {
$0.text = "▶"
}
private lazy var foldButton: UIButton = .init(type: .system).then {
$0.addTarget(self, action: #selector(foldTapped), for: .touchUpInside)
$0.backgroundColor = .secondarySystemBackground
$0.layer.cornerRadius = 10
$0.layer.masksToBounds = true
$0.addSubview(dotsButton)
dotsButton.snp.makeConstraints {
$0.top.bottom.equalToSuperview()
$0.trailing.equalToSuperview().inset(10)
}
$0.addSubview(arrowLabel)
arrowLabel.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.leading.equalToSuperview().inset(20)
}
$0.addSubview(titleLabel)
titleLabel.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.leading.equalTo(arrowLabel.snp.trailing).offset(10)
$0.trailing.lessThanOrEqualTo(dotsButton.snp.leading).offset(-10)
}
}
private lazy var dotsButton: UIButton = .init(type: .system).then {
$0.setTitle("···", for: .normal)
$0.titleLabel?.font = .preferredFont(forTextStyle: .title3)
$0.addTarget(self, action: #selector(dotsTapped), for: .touchUpInside)
}
init(isFolded: Bool = true, components: [UIView] = []) {
self.isFolded = isFolded
self.components = components
super.init(frame: .zero)
setUp()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setUp() {
self.isFolded = isFolded ? true : false // to call initial didSet
containerView.addGestureRecognizer(tapGesture)
let stackView = UIStackView().then {
$0.axis = .vertical
$0.distribution = .fill
$0.alignment = .center
}
stackView.addArrangedSubview(foldButton)
stackView.addArrangedSubview(containerView)
stackView.bringSubviewToFront(foldButton)
addSubview(stackView)
stackView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
foldButton.snp.makeConstraints {
$0.width.equalToSuperview()
$0.centerX.equalToSuperview()
}
containerView.snp.makeConstraints {
$0.width.equalToSuperview().inset(10)
$0.centerX.equalToSuperview()
}
containerView.axis = .vertical
setComponents(with: components)
}
func setComponents(with components: [UIView]) {
containerView.arrangedSubviews.forEach { v in
v.removeFromSuperview()
}
components.forEach { v in
containerView.addArrangedSubview(v)
}
self.components = components
}
@objc private func foldTapped(_ sender: UIButton) {
isFolded.toggle()
}
@objc private func dotsTapped() {
dotsButtonHandler?()
}
@objc private func componentTapped(recognizer: UITapGestureRecognizer) {
guard let tappedView = containerView.arrangedSubviews.first(where: { v in
let tappedPoint = recognizer.location(in: v)
return v.point(inside: tappedPoint, with: nil)
}) else {
return
}
componentTappedHandler?(tappedView)
}
}
@kyungpyoda
Copy link
Author

kyungpyoda commented Jul 14, 2021

ezgif com-video-to-gif-3 ezgif com-video-to-gif-2

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