I made a custom UIButton which have a collapse UITableView in it. Once you tap the button the table view height constraint change and the table view opens with collapse effect. I managed to create delegate to the button which provide the table's data but I can't get the didSelectRow and scroll the tableView. I think it may related to the need to enlarge the button frame when the tableView collapse but I didn't managed to do that.
Here's the custom UIButton:
CollapseButton.swift
Created by Matan Levi on 21/12/2020.
import UIKit
protocol CollapseButtonDelegate
{
var options : [String] { get }
func didSelectRow(tableView: UITableView, indexPath: IndexPath)
}
@IBDesignable class CollapseButton: UIButton {
@IBOutlet weak var btnTableViewOutlet: UITableView!
@IBOutlet weak var btnCollapseHeight: NSLayoutConstraint!
let open = CGFloat(113)
let close = CGFloat.zero
var isOpen = false
var delegate : CollapseButtonDelegate?
// init used if the view is created programmatically
override init(frame: CGRect) {
super.init(frame: frame)
self.customInit()
}
// init used if the view is created through IB
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.customInit()
}
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
guard subviews.isEmpty else { return self }
let view = UINib(nibName: "CollapseButton", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
view.translatesAutoresizingMaskIntoConstraints = false
return view
}
// Do custom initialization here
private func customInit()
{
}
@IBAction func collapseBtn(_ sender: UIButton) {
self.btnTableViewOutlet.addBorder()
UIView.animate(withDuration: 1) {
self.btnCollapseHeight.constant = self.isOpen ? self.close : self.open
sender.imageView?.transform = self.isOpen ? CGAffineTransform.identity : CGAffineTransform(rotationAngle: .pi)
self.layoutIfNeeded()
}
isOpen = !isOpen
}
}
extension CollapseButton : UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return delegate?.options.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = delegate?.options[indexPath.row]
cell.backgroundColor = .green
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.didSelectRow(tableView: tableView, indexPath: indexPath)
}
}
And here's the button implementation:
extension PortfolioViewController : CollapseButtonDelegate
{
func didOpenCollapse() {
accountBtnOutlet.setNeedsUpdateConstraints()
accountBtnOutlet.addBorder()
}
var options: [String] {
return FakeDataSupplier.shared.getAccountsData()
}
func didSelectRow(tableView: UITableView, indexPath: IndexPath) {
print(indexPath.row)
}
}
I tried:
1.Checking user interactions enabled 2.Checking that the delegate and data source connected 3.Checking that there's nothing above the UITableView in the ui debugger
Any suggestion would be very appreciated
EDIT
Added screenshot:
So the actual solution went that way: at first iv'e changed my class to UIView, which make things way more convenient in terms of adding more views or tables in my case. After that i made delegate for the opening of the table and in the viewController that using the button i made an height constraint outlet to the button and updated it when that button open the collapse table. Thanks to @DonMag for guiding me to investigate the solution.
I will try make it more simple and easy to use but until then, here's the semi final buttonView class:
//
// CollapseView.swift
// IBITrade
//
// Created by Matan Levi IBI on 22/12/2020.
//
import UIKit
protocol CollapseViewDelegate
{
func setTableViewOptions(collapseView : CollapseView) -> [String]
func didSelectRow(collapseView : CollapseView, tableView: UITableView, indexPath: IndexPath)
func isCollapseOpen(collapseView : CollapseView, isOpen : Bool)
}
@IBDesignable class CollapseView: UIView {
@IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var collapseBtnOutlet: UIButton!
@IBOutlet weak var collapseTableViewOutlet: UITableView!
public var buttonTitle: String = "" {
didSet {
self.collapseBtnOutlet.setTitle(buttonTitle, for: .normal)
}
}
var delegate : CollapseViewDelegate?
let open = CGFloat(113)
let close = CGFloat.zero
var isOpen = false
// init used if the view is created programmatically
let nibName = "CollapseView"
var contentView:UIView?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
@IBAction func openCloseTable(_ sender: UIButton) {
collapseTableViewOutlet.addBorder()
UIView.animate(withDuration: 1) { [self] in
self.tableViewHeightConstraint.constant = self.isOpen ? self.close : self.open
sender.imageView?.transform = self.isOpen ? CGAffineTransform.identity : CGAffineTransform(rotationAngle: .pi)
delegate?.isCollapseOpen(collapseView: self, isOpen: isOpen)
self.layoutIfNeeded()
}
isOpen = !isOpen
}
}
extension CollapseView : UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return delegate?.setTableViewOptions(collapseView: self).count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = delegate?.setTableViewOptions(collapseView: self)[indexPath.row]
cell.backgroundColor = .green
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.didSelectRow(collapseView: self, tableView: tableView, indexPath: indexPath)
}
}