Search code examples
iosswiftxcodepanelfloating

FloatingPanel not appearing - Swift Xcode


I have a view controller which contains a table view. In this view controller, I've also set up a floating panel. The floating panel is not appearing for some reason, however. I know the ContentViewController is working, so I didn't include that code. The error is somewhere in this code and I can't find it. It's like the FloatingPanel is just not being added to the view.

import Foundation
import UIKit
import FloatingPanel

class Section {
    let title: String
    let options: [String]
    var isOpened: Bool = false
    
    init(title: String,
         options: [String],
         isOpened: Bool
    ) {
        self.title = title
        self.options = options
        self.isOpened = isOpened
    }
    
}

class PicsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UINavigationControllerDelegate, FloatingPanelControllerDelegate {
    
    @IBOutlet weak var addButton: UIButton!
    
    
    private let picsTableView: UITableView = {
        let picsTableView = UITableView()
        picsTableView.register(UITableViewCell.self,
                               forCellReuseIdentifier: "cell")
        return picsTableView
    }()
    
    private var sections = [Section]()
    let backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    
    
    /*------------START FLOATING PANEL-------------*/
    var fpc: FloatingPanelController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("PicsViewController LAUNCHED!!")
        fpc = FloatingPanelController()
        fpc.delegate = self
//        guard let contentVC = storyboard?.instantiateViewController(identifier: "fpc_content") as? ContentViewController else {
//            print("Failed to instantiate storyboard")
//            return
//        }
        let contentVC = ContentViewController()
        
        fpc.set(contentViewController: contentVC)
//        fpc.track(scrollView: contentVC.collectionView)
        fpc.addPanel(toParent: self)
        
    /*------------END FLOATING PANEL-------------*/
        
        
        // Set up models
        picsTableView.delegate = self
        picsTableView.dataSource = self
        picsTableView.frame = view.bounds
        picsTableView.backgroundColor = backgroundColor
        view.addSubview(picsTableView)
        sections = [
            Section(title: "Section 1", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
            Section(title: "Section 2", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
            Section(title: "Section 3", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
            Section(title: "Section 4", options: ["Test 1", "Test 2", "Test 3"], isOpened: false)
        ]
        // get rid of extra table view cells
        picsTableView.tableFooterView = UIView()
        
        // customize button
        addButton.layer.cornerRadius = 0.5 * addButton.bounds.size.width
        addButton.clipsToBounds = true
        addButton.backgroundColor = .red
        addButton.tintColor = .white
        addButton.setImage(#imageLiteral(resourceName: "plus-5-512"), for: .normal)
        view.bringSubviewToFront(addButton)
        
    }
    
    
    @IBAction func addButtonTapped(_ sender: Any) {
        print("Add button tapped")
    }
    
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let section = sections[section]
        
        if section.isOpened {
            return section.options.count + 1
        } else {
            return 1
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = picsTableView.dequeueReusableCell(
            withIdentifier: "cell",
            for: indexPath
        )
        cell.backgroundColor = .clear
        cell.textLabel?.textColor = .black
        
        if indexPath.row == 0 {
            cell.textLabel?.text = sections[indexPath.section].title
        } else {
            cell.textLabel?.text = sections[indexPath.section].options[indexPath.row - 1]
        }
        
        return cell
    }
    
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        picsTableView.deselectRow(at: indexPath, animated: true)
        
        if indexPath.row == 0 {
            sections[indexPath.section].isOpened = !sections[indexPath.section].isOpened
            picsTableView.reloadSections([indexPath.section], with: .none)
        } else {
            print("Tapped sub cell \(indexPath.row)")
        }
    }
    
    
}

Solution

  • Typically, 3 or 4 statements to add any view controller as subview to any view controllers are

            let viewController = SomeViewController()
            self.addChild(viewController)
            self.view.addSubview(viewController.view)
            viewController.didMove(toParent: self)
    

    Because you havent provided the implementation for addPanel where you are calling fpc.addPanel(toParent: self), I assume it does something similar to what I have written above, if I were to write it, I might write something like

        func addPanel(toParent: UIViewController) {
            toParent.addChild(self)
            toParent.view.addSubview(self.view)
            //add constraints or set frames for your subview
            self.didMove(toParent: toParent)
        }
    

    Finally, you are adding multiple view's to your view controller's view namely a floating panel, tableView, and a button. Because you add panel before you add all other views it might be hidden under other subviews like tableView, you can obviously check this hypothesis using view debugger, and simplest way to fix it would be to use bringSubviewToFront, but issue is you are not holding the reference of FloatingPanelController's view so you can try adding self.view.bringSubviewToFront(fpc.view) as last statement of ViewDidLoad

    override func viewDidLoad() {
            super.viewDidLoad()
            // all other codes of yours
            self.view.bringSubviewToFront(fpc.view)
    }
    

    and for some reason if that doesnt work call fpc.addPanel(toParent: self) as last statement of ViewDidLoad instead of adding in between and add bringSubviewToFront as last statement of addPanel(toParent method

        override func viewDidLoad() {
            super.viewDidLoad()
            print("PicsViewController LAUNCHED!!")
            fpc = FloatingPanelController()
            fpc.delegate = self
            let contentVC = ContentViewController()
    
            fpc.set(contentViewController: contentVC)
            picsTableView.delegate = self
            picsTableView.dataSource = self
            picsTableView.frame = view.bounds
            picsTableView.backgroundColor = backgroundColor
            view.addSubview(picsTableView)
            sections = [
                Section(title: "Section 1", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
                Section(title: "Section 2", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
                Section(title: "Section 3", options: ["Test 1", "Test 2", "Test 3"], isOpened: false),
                Section(title: "Section 4", options: ["Test 1", "Test 2", "Test 3"], isOpened: false)
            ]
            // get rid of extra table view cells
            picsTableView.tableFooterView = UIView()
    
            // customize button
            addButton.layer.cornerRadius = 0.5 * addButton.bounds.size.width
            addButton.clipsToBounds = true
            addButton.backgroundColor = .red
            addButton.tintColor = .white
            addButton.setImage(#imageLiteral(resourceName: "plus-5-512"), for: .normal)
            view.bringSubviewToFront(addButton)
    
            fpc.addPanel(toParent: self) //added as last statement
        }
    

    and

    func addPanel(toParent: UIViewController) {
            toParent.addChild(self)
            toParent.view.addSubview(self.view)
            self.didMove(toParent: toParent)
            toParent.view.bringSubviewToFront(self.view)
    }