Insert a floating action button on UITableView in Swift

15,383

Solution 1

If you currently using tableViewController then no , you must subclass UIViewController add UItableView and your floating button to it

Or you may override scollviewDidScroll and change button y according to tableview current offset

drag scrollview as IBOutlet and set it's delegate to the viewController

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

       let  off = scrollView.contentOffset.y

       btn.frame = CGRect(x: 285, y:   off + 485, width: btn.frame.size.width, height: btn.frame.size.height)
}

code snapshot

enter image description here

in action

enter image description here see in action

Solution 2

All subviews added to UITableView will automatically scroll with it.

What you can do is add the button to the application Window, just remember to remove it when the ViewController disappears.

var btn = UIButton(type: .custom)
func floatingButton(){
    btn.frame = CGRect(x: 285, y: 485, width: 100, height: 100)
    btn.setTitle("All Defects", for: .normal)
    btn.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
    btn.clipsToBounds = true
    btn.layer.cornerRadius = 50
    btn.layer.borderColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    btn.layer.borderWidth = 3.0
    btn.addTarget(self,action: #selector(DestinationVC.buttonTapped), for: UIControlEvent.touchUpInside)
    if let window = UIApplication.shared.keyWindow {
        window.addSubview(btn)
    }
}

and in viewWillDisappear:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    btn.removeFromSuperview()
}

For iOS 13 and above you can check for the key window using this extension:

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}

Solution 3

Here's a working example on XCode 10 with Swift 4.2.

Note that the FAB button will disappear when the view disappears, and reappears when the controller is loaded again.

import UIKit

class ViewController: UITableViewController {

lazy var faButton: UIButton = {
    let button = UIButton(frame: .zero)
    button.translatesAutoresizingMaskIntoConstraints = false
    button.backgroundColor = .blue
    button.addTarget(self, action: #selector(fabTapped(_:)), for: .touchUpInside)
    return button
}()

override func viewDidLoad() {
    super.viewDidLoad()
    setupTableView()
}

override func viewDidAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if let view = UIApplication.shared.keyWindow {
        view.addSubview(faButton)
        setupButton()
    }
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if let view = UIApplication.shared.keyWindow, faButton.isDescendant(of: view) {
        faButton.removeFromSuperview()
    }
}

func setupTableView() {
    tableView.backgroundColor = .darkGray
}

func setupButton() {
    NSLayoutConstraint.activate([
        faButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -36),
        faButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -36),
        faButton.heightAnchor.constraint(equalToConstant: 80),
        faButton.widthAnchor.constraint(equalToConstant: 80)
        ])
    faButton.layer.cornerRadius = 40
    faButton.layer.masksToBounds = true
    faButton.layer.borderColor = UIColor.lightGray.cgColor
    faButton.layer.borderWidth = 4
}

@objc func fabTapped(_ button: UIButton) {
    print("button tapped")
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 5
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return UITableViewCell()
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 64
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 32
}

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return UIView()
}
}
Share:
15,383
Saurabh
Author by

Saurabh

Updated on June 12, 2022

Comments

  • Saurabh
    Saurabh almost 2 years

    I'm trying to add a floating action button (not a floating menu button) which will navigate me to the next view controller with a single click. I'm not getting the floating button right. I have tried the below code and it is not showing the appropriate button on the table view as it is getting scrolled along with the table. Is there any way to stick the button at the same place without getting scrolled along with the table?

    func floatingButton(){
        let btn = UIButton(type: .custom)
        btn.frame = CGRect(x: 285, y: 485, width: 100, height: 100)
        btn.setTitle("All Defects", for: .normal)
        btn.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
        btn.clipsToBounds = true
        btn.layer.cornerRadius = 50
        btn.layer.borderColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        btn.layer.borderWidth = 3.0
        btn.addTarget(self,action: #selector(DestinationVC.buttonTapped), for: UIControlEvent.touchUpInside)
        view.addSubview(btn)
    }
    

    enter image description here