Add Expandable States

This commit is contained in:
Ivan Zinovyev 2019-01-22 03:23:46 +03:00
parent 0aa988d60d
commit 83de05fde7
11 changed files with 198 additions and 66 deletions

View File

@ -3,25 +3,41 @@ import TableKit
class BaseExpandableCellViewModel: ExpandableCellViewModel {
// MARK: - Properties
let index: Int
let width: CGFloat
let text: String = .random(length: .random(in: 50...400))
let text: String = .random(length: .random(in: 100...400))
// MARK: - Colors
let collapsedColor = UIColor.random()
let expandedColor = UIColor.random()
// MARK: - ExpandableCellViewModel
var isCollapsed = true
var expandableState: ExpandableState = .expanded
static let oneLineState: ExpandableState = .height(value: BaseTableViewCell.collapsedHeight + 40)
static let twoLinesState: ExpandableState = .height(value: BaseTableViewCell.collapsedHeight + 60)
static let threeLinesState: ExpandableState = .height(value: BaseTableViewCell.collapsedHeight + 80)
var availableStates: [ExpandableState] = [
.collapsed,
oneLineState,
twoLinesState,
threeLinesState,
.expanded
]
// MARK: - Life Cycle
init(index: Int) {
init(index: Int,
width: CGFloat) {
self.index = index
self.width = width
}
}

View File

@ -19,6 +19,16 @@ class BaseTableViewCell: UITableViewCell {
let label = UILabel()
let indexLabel = UILabel()
let collapsedStateButton = UIButton()
let oneLineButton = UIButton()
let twoLinesButton = UIButton()
let threeLinesButton = UIButton()
let expandedStateButton = UIButton()
// MARK: - Properties
@ -35,18 +45,17 @@ class BaseTableViewCell: UITableViewCell {
label.numberOfLines = 0
collapsedView.addSubview(indexLabel)
collapsedView.addSubview(collapsedStateButton)
collapsedView.addSubview(expandedStateButton)
collapsedView.addSubview(oneLineButton)
collapsedView.addSubview(twoLinesButton)
collapsedView.addSubview(threeLinesButton)
containerView.addSubview(collapsedView)
containerView.addSubview(label)
contentView.addSubview(containerView)
initializeView()
}
override func layoutSubviews() {
super.layoutSubviews()
label.preferredMaxLayoutWidth = contentView.frame.width - 2 * BaseTableViewCell.textMargin
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
@ -55,7 +64,17 @@ class BaseTableViewCell: UITableViewCell {
// MARK: - To Override
func initializeView() {
collapsedStateButton.setTitle("Collapsed", for: .normal)
oneLineButton.setTitle("One Line", for: .normal)
twoLinesButton.setTitle("Two Lines", for: .normal)
threeLinesButton.setTitle("Three Lines", for: .normal)
expandedStateButton.setTitle("Expanded", for: .normal)
[collapsedStateButton, oneLineButton, twoLinesButton, threeLinesButton, expandedStateButton]
.forEach { button in
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 10)
}
}
}

View File

@ -1,14 +1,48 @@
import SnapKit
import TableKit
private extension CGFloat {
static let spaceBetweenStateButtons: CGFloat = 10
}
final class ExpandableAutolayoutCell: BaseTableViewCell, ConfigurableCell, Expandable {
// MARK: - Init
override func initializeView() {
super.initializeView()
collapsedView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(expand)))
collapsedStateButton.addTarget(self, action: #selector(toCollapsed), for: .touchUpInside)
oneLineButton.addTarget(self, action: #selector(toOneLine), for: .touchUpInside)
twoLinesButton.addTarget(self, action: #selector(toTwoLines), for: .touchUpInside)
threeLinesButton.addTarget(self, action: #selector(toThreeLines), for: .touchUpInside)
expandedStateButton.addTarget(self, action: #selector(toExpanded), for: .touchUpInside)
makeConstraints()
}
@objc func toCollapsed() {
transition(to: .collapsed)
}
@objc func toOneLine() {
transition(to: BaseExpandableCellViewModel.oneLineState)
}
@objc func toTwoLines() {
transition(to: BaseExpandableCellViewModel.twoLinesState)
}
@objc func toThreeLines() {
transition(to: BaseExpandableCellViewModel.threeLinesState)
}
@objc func toExpanded() {
transition(to: .expanded)
}
// MARK: - Actions
@ -22,18 +56,18 @@ final class ExpandableAutolayoutCell: BaseTableViewCell, ConfigurableCell, Expan
// MARK: - Expandable
func configureAppearance(isCollapsed: Bool) {
func configure(state: ExpandableState) {
guard let viewModel = viewModel else {
return
}
heightConstraint?.layoutConstraints.first?.constant = isCollapsed
heightConstraint?.layoutConstraints.first?.constant = state.isCollapsed
? BaseTableViewCell.collapsedHeight
: label.frame.maxY + BaseTableViewCell.bottomMargin
: state.height ?? (label.frame.maxY + BaseTableViewCell.bottomMargin)
containerView.backgroundColor = isCollapsed ? viewModel.collapsedColor : viewModel.expandedColor
containerView.backgroundColor = state.isCollapsed ? viewModel.collapsedColor : .random()
label.alpha = isCollapsed ? 0 : 1
label.alpha = state.isCollapsed ? 0 : 1
}
// MARK: - ConfigurableCell
@ -43,6 +77,8 @@ final class ExpandableAutolayoutCell: BaseTableViewCell, ConfigurableCell, Expan
label.text = viewModel.text
indexLabel.text = String(viewModel.index)
label.preferredMaxLayoutWidth = viewModel.width - 2 * BaseTableViewCell.textMargin
initState()
}
@ -62,6 +98,11 @@ private extension ExpandableAutolayoutCell {
makeCollapsedViewConstraints()
makeLabelConstraints()
makeIndexLabelConstraints()
makeCollapsedStateButtonConstraints()
makeOneLineButtonConstraints()
makeTwoLinesButtonConstraints()
makeThreeLinesButtonConstraints()
makeExpandedStateButtonConstraints()
}
func makeContainerViewConstraints() {
@ -91,5 +132,40 @@ private extension ExpandableAutolayoutCell {
make.top.leading.equalToSuperview()
}
}
func makeCollapsedStateButtonConstraints() {
collapsedStateButton.snp.remakeConstraints { make in
make.firstBaseline.equalTo(indexLabel.snp.firstBaseline)
make.leading.equalTo(indexLabel.snp.trailing).offset(CGFloat.spaceBetweenStateButtons)
}
}
func makeOneLineButtonConstraints() {
oneLineButton.snp.remakeConstraints { make in
make.firstBaseline.equalTo(indexLabel.snp.firstBaseline)
make.leading.equalTo(collapsedStateButton.snp.trailing).offset(CGFloat.spaceBetweenStateButtons)
}
}
func makeTwoLinesButtonConstraints() {
twoLinesButton.snp.remakeConstraints { make in
make.firstBaseline.equalTo(indexLabel.snp.firstBaseline)
make.leading.equalTo(oneLineButton.snp.trailing).offset(CGFloat.spaceBetweenStateButtons)
}
}
func makeThreeLinesButtonConstraints() {
threeLinesButton.snp.remakeConstraints { make in
make.firstBaseline.equalTo(indexLabel.snp.firstBaseline)
make.leading.equalTo(twoLinesButton.snp.trailing).offset(CGFloat.spaceBetweenStateButtons)
}
}
func makeExpandedStateButtonConstraints() {
expandedStateButton.snp.remakeConstraints { make in
make.firstBaseline.equalTo(indexLabel.snp.firstBaseline)
make.leading.equalTo(threeLinesButton.snp.trailing).offset(CGFloat.spaceBetweenStateButtons)
}
}
}

View File

@ -5,6 +5,8 @@ final class ExpandableManualLayoutCell: BaseTableViewCell, ConfigurableCell, Exp
// MARK: - Init
override func initializeView() {
super.initializeView()
collapsedView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(expand)))
}
@ -18,40 +20,45 @@ final class ExpandableManualLayoutCell: BaseTableViewCell, ConfigurableCell, Exp
override func layoutSubviews() {
super.layoutSubviews()
guard let viewModel = viewModel else {
return
}
collapsedView.frame = CGRect(x: 0,
y: 0,
width: UIScreen.main.bounds.width,
width: viewModel.width,
height: BaseTableViewCell.collapsedHeight)
indexLabel.frame = CGRect(x: 0,
y: 0,
width: UIScreen.main.bounds.width,
width: viewModel.width,
height: 0)
indexLabel.sizeToFit()
label.frame = CGRect(x: BaseTableViewCell.textMargin,
y: collapsedView.frame.maxY + BaseTableViewCell.textMargin,
width: UIScreen.main.bounds.width - BaseTableViewCell.textMargin * 2,
width: viewModel.width - BaseTableViewCell.textMargin * 2,
height: 0)
label.sizeToFit()
}
// MARK: - Expandable
func configureAppearance(isCollapsed: Bool) {
func configure(state: ExpandableState) {
guard let viewModel = viewModel else {
return
}
containerView.frame = CGRect(x: 0,
y: 0,
width: UIScreen.main.bounds.width,
height: isCollapsed ? BaseTableViewCell.collapsedHeight : (label.frame.maxY + BaseTableViewCell.bottomMargin))
let height = state.isCollapsed
? BaseTableViewCell.collapsedHeight
: state.height ?? (label.frame.maxY + BaseTableViewCell.bottomMargin)
containerView.frame = CGRect(x: 0, y: 0, width: viewModel.width, height: height)
containerView.backgroundColor = isCollapsed ? viewModel.collapsedColor : viewModel.expandedColor
containerView.backgroundColor = state.isCollapsed ? viewModel.collapsedColor : .random()
label.alpha = isCollapsed ? 0 : 1
label.alpha = state.isCollapsed ? 0 : 1
}
// MARK: - ConfigurableCell

View File

@ -2,10 +2,12 @@ import TableKit
import PinLayout
final class ExpandablePinLayoutCell: BaseTableViewCell, ConfigurableCell, Expandable {
// MARK: - Init
override func initializeView() {
super.initializeView()
collapsedView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(expand)))
}
@ -19,12 +21,22 @@ final class ExpandablePinLayoutCell: BaseTableViewCell, ConfigurableCell, Expand
override func layoutSubviews() {
super.layoutSubviews()
indexLabel.pin
.left()
guard let viewModel = viewModel else {
return
}
containerView.pin
.top()
.sizeToFit()
.horizontally()
.width(viewModel.width)
collapsedView.pin
.top()
.left()
.right()
.height(BaseTableViewCell.collapsedHeight)
label.pin
.below(of: collapsedView)
.marginTop(BaseTableViewCell.textMargin)
@ -32,29 +44,28 @@ final class ExpandablePinLayoutCell: BaseTableViewCell, ConfigurableCell, Expand
.right(BaseTableViewCell.textMargin)
.sizeToFit(.width)
collapsedView.pin
.top()
indexLabel.pin
.left()
.right()
.height(BaseTableViewCell.collapsedHeight)
.top()
.sizeToFit()
}
// MARK: - Expandable
func configureAppearance(isCollapsed: Bool) {
func configure(state: ExpandableState) {
guard let viewModel = viewModel else {
return
}
let height = state.isCollapsed
? BaseTableViewCell.collapsedHeight
: state.height ?? (label.frame.maxY + BaseTableViewCell.bottomMargin)
containerView.pin.height(height)
containerView.backgroundColor = state.isCollapsed ? viewModel.collapsedColor : .random()
containerView.pin
.top()
.left()
.right()
.height(isCollapsed ? BaseTableViewCell.collapsedHeight : (label.frame.maxY + BaseTableViewCell.bottomMargin))
containerView.backgroundColor = isCollapsed ? viewModel.collapsedColor : viewModel.expandedColor
label.alpha = isCollapsed ? 0 : 1
label.alpha = state.isCollapsed ? 0 : 1
}
// MARK: - ConfigurableCell
@ -63,6 +74,7 @@ final class ExpandablePinLayoutCell: BaseTableViewCell, ConfigurableCell, Expand
self.viewModel = viewModel
label.text = viewModel.text
indexLabel.text = String(viewModel.index)
initState()

View File

@ -6,10 +6,10 @@ class AutoLayoutViewController: BaseViewController {
return "Auto Layout"
}
override var rows: [Row] {
override func rows(width: CGFloat) -> [Row] {
return Array(0...100)
.map {
TableRow<ExpandableAutolayoutCell>(item: BaseExpandableCellViewModel(index: $0))
TableRow<ExpandableAutolayoutCell>(item: BaseExpandableCellViewModel(index: $0, width: width))
}
}

View File

@ -13,11 +13,12 @@ class BaseViewController: UIViewController {
return ""
}
var rows: [Row] {
func rows(width: CGFloat) -> [Row] {
return []
}
private lazy var tableDirector = TableDirector(tableView: tableView)
private lazy var tableDirector = TableDirector(tableView: tableView,
cellHeightCalculator: ExpandableCellHeightCalculator(tableView: tableView))
// MARK: - Life Cycle
@ -30,13 +31,14 @@ class BaseViewController: UIViewController {
tableView.separatorStyle = .none
navigationItem.title = layoutType
let width = view.frame.width
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
self?.loadRows()
self?.loadRows(width: width)
}
}
func loadRows() {
let section = TableSection(onlyRows: rows)
func loadRows(width: CGFloat) {
let section = TableSection(onlyRows: rows(width: width))
DispatchQueue.main.async { [weak self] in
self?.tableDirector.replace(withSections: [section])

View File

@ -6,10 +6,10 @@ class ManualLayoutViewController: BaseViewController {
return "Manual Layout"
}
override var rows: [Row] {
override func rows(width: CGFloat) -> [Row] {
return Array(0...100)
.map {
TableRow<ExpandableManualLayoutCell>(item: BaseExpandableCellViewModel(index: $0))
TableRow<ExpandableManualLayoutCell>(item: BaseExpandableCellViewModel(index: $0, width: width))
}
}

View File

@ -6,10 +6,10 @@ class PinLayoutViewController: BaseViewController {
return "Pin Layout"
}
override var rows: [Row] {
override func rows(width: CGFloat) -> [Row] {
return Array(0...100)
.map {
TableRow<ExpandablePinLayoutCell>(item: BaseExpandableCellViewModel(index: $0))
TableRow<ExpandablePinLayoutCell>(item: BaseExpandableCellViewModel(index: $0, width: width))
}
}

View File

@ -5,7 +5,7 @@ inhibit_all_warnings!
target 'ExpandableCellDemo-ios' do
pod 'TableKit', :git => 'https://github.com/TouchInstinct/TableKit', :commit => '6ef5ad504eb3305db32ba98377bd896474914943'
pod 'TableKit', :git => 'https://github.com/TouchInstinct/TableKit', :commit => 'ff18a4d8b84e5970963e69968bc17399c8c855c4'
pod 'PinLayout'
pod 'SnapKit'

View File

@ -1,12 +1,12 @@
PODS:
- PinLayout (1.8.6)
- SnapKit (4.2.0)
- TableKit (2.8.0)
- TableKit (2.8.1)
DEPENDENCIES:
- PinLayout
- SnapKit
- TableKit (from `https://github.com/TouchInstinct/TableKit`, commit `6ef5ad504eb3305db32ba98377bd896474914943`)
- TableKit (from `https://github.com/TouchInstinct/TableKit`, commit `ff18a4d8b84e5970963e69968bc17399c8c855c4`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
@ -15,19 +15,19 @@ SPEC REPOS:
EXTERNAL SOURCES:
TableKit:
:commit: 6ef5ad504eb3305db32ba98377bd896474914943
:commit: ff18a4d8b84e5970963e69968bc17399c8c855c4
:git: https://github.com/TouchInstinct/TableKit
CHECKOUT OPTIONS:
TableKit:
:commit: 6ef5ad504eb3305db32ba98377bd896474914943
:commit: ff18a4d8b84e5970963e69968bc17399c8c855c4
:git: https://github.com/TouchInstinct/TableKit
SPEC CHECKSUMS:
PinLayout: fe2a2432d6982588e208572005c941aeeae417ab
SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a
TableKit: d635663343d00e209f258e35d4ee0072ad1beb1a
TableKit: 18a0049dea981c1106409bdeebc763ef74d36f02
PODFILE CHECKSUM: 6b497b1c0f4030ade5783db0b837662f14759131
PODFILE CHECKSUM: edc056e576a26e481d1110118f841d78d997b954
COCOAPODS: 1.6.0.beta.2