2

I'm trying to find a good way to add simple UIButton subview with an action to some UIViewControllers.

I think it could be working with protocol and its extension, but the extension does not allow the @objc methods, It means, I can't add target with Selector.

I created another class to solve this @objc problem, but I could't add the view as a parameter...

I also created an UIViewController subclass and it's working perfectly, but I'm not sure that it is a good idea to implement a lot UIViewControllers with that subclass.

So the question is, what is the best solution to add subviews with action to more than one UIViewController?

see my code below:

public protocol ImplementNewRightIcon {
    func setupNewBottomRightMenu()
}

public extension ImplementNewRightIcon where Self: UIViewController {

func setupNewBottomRightMenu() {

    let buttonwith: CGFloat = 50

    let button: UIButton = {
       let btn = UIButton()
        btn.setImage(#imageLiteral(resourceName: "icons8-plus-math-50_white"), for: .normal)
        btn.backgroundColor = .red
        btn.clipsToBounds = true
        btn.layer.cornerRadius = buttonwith * 0.5
        btn.imageEdgeInsets = UIEdgeInsetsMake(10,10,10,10)
        btn.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
        btn.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
        btn.layer.shadowOpacity = 0.5
        btn.layer.shadowRadius = 0.0
        btn.layer.masksToBounds = false
        btn.addTarget(SetupMenuLayer.sharedInstance, action: #selector(SetupMenuLayer.showMenuButtonDidTapped(SenderViewController:)), for: .touchUpInside)
        return btn;
    }()

    self.view.addSubview(button)
    button.anchor(top: nil, left: nil, bottom: self.view.bottomAnchor, right: self.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 20, paddingRight: 20, width: buttonwith, height: buttonwith)

}

}

class SetupMenuLayer: NSObject {
static let sharedInstance = SetupMenuLayer()

@objc func showMenuButtonDidTapped(SenderViewController: UIViewController) {

    let bottomMenuView: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        return view
    }()

    SenderViewController.view.addSubview(bottomMenuView)
    bottomMenuView.anchor(top: nil, left: SenderViewController.view.leftAnchor, bottom: SenderViewController.view.bottomAnchor, right: SenderViewController.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: SenderViewController.view.frame.width, height: SenderViewController.view.frame.height / 3)

}

}

class ImplementNewRightIconView: UIViewController {

override func viewDidLoad() {
    self.setupNewRightIconBtn()
}

func setupNewRightIconBtn() {
    let buttonwith: CGFloat = 50

    let button: UIButton = {
        let btn = UIButton()
        btn.setImage(#imageLiteral(resourceName: "icons8-plus-math-50_white"), for: .normal)
        btn.backgroundColor = .red
        btn.clipsToBounds = true
        btn.layer.cornerRadius = buttonwith * 0.5
        btn.imageEdgeInsets = UIEdgeInsetsMake(10,10,10,10)
        btn.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
        btn.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
        btn.layer.shadowOpacity = 0.5
        btn.layer.shadowRadius = 0.0
        btn.layer.masksToBounds = false
        //it works
        btn.addTarget(self, action: #selector(showMenuButtonDidTapped), for: .touchUpInside)
        return btn;
    }()

    self.view.addSubview(button)
    button.anchor(top: nil, left: nil, bottom: self.view.bottomAnchor, right: self.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 20, paddingRight: 20, width: buttonwith, height: buttonwith)
}

@objc func showMenuButtonDidTapped() {

    let bottomMenuView: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        return view
    }()

    self.view.addSubview(bottomMenuView)
    bottomMenuView.anchor(top: nil, left: self.view.leftAnchor, bottom: self.view.bottomAnchor, right: self.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: self.view.frame.width, height: self.view.frame.height / 3)

}

}

3 Answers3

0

If you want to Add more than one UIViewControllers as a subview in the same ViewController then definitely you need to Use Container View.

Nikunj Kumbhani
  • 3,758
  • 2
  • 26
  • 51
0

You can make extension for UIButton and add method with following semantics:

func addTergetClosure(didTouch: () -> Void)

didTouch will be called on touchUpInside event, details here how you can make it. Next, you just need to make a protocol with Self constraint to UIViewController as you did, which will add your button as subview. And I'd add button property as a protocol part, because you will may need to access it somewhere in those viewcontrollers, not only add button and closure which will be invoked on touchUpInside.

So it will be like that:

protocol MyProtocol { 
   var button: UIButton { get set }
   func setButton()
}

extension MyProtocol where Self: UIViewController {
   func setButton() { ... }
}

If your buttons look alike over ViewControllers than I'd recommend you to separate that logic to UIButton extension with static fields which will return UIButton with the styles you need. In provided solution, you will just set button in ViewController like

class MyController: UIViewController, MyProtocol {
   var button = UIButton.shadowed
}

extension UIButton {
    static let shadowed: UIButton = { ... }
}
Mikhail Maslo
  • 616
  • 6
  • 13
0

If you want multiple view controllers to have shared functionality, or in your specific case always have this button added and in the same place, you should create a base view controller for them to inherit from.

class ViewControllerWithMenu: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupNewBottomRightMenu()   
    }

    func setupNewBottomRightMenu() {

    let buttonwith: CGFloat = 50

    let button: UIButton = {
       let btn = UIButton()
        btn.setImage(#imageLiteral(resourceName: "icons8-plus-math-50_white"), for: .normal)
        btn.backgroundColor = .red
        btn.clipsToBounds = true
        btn.layer.cornerRadius = buttonwith * 0.5
        btn.imageEdgeInsets = UIEdgeInsetsMake(10,10,10,10)
        btn.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
        btn.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
        btn.layer.shadowOpacity = 0.5
        btn.layer.shadowRadius = 0.0
        btn.layer.masksToBounds = false
        btn.addTarget(SetupMenuLayer.sharedInstance, action: #selector(SetupMenuLayer.showMenuButtonDidTapped(SenderViewController:)), for: .touchUpInside)
             return btn;
        }()

        self.view.addSubview(button)
        button.anchor(top: nil, left: nil, bottom: self.view.bottomAnchor, right: 
        self.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 20, paddingRight: 20, width: buttonwith, height: buttonwith)

    }

}

Now you do not need to call anything in each view controller, just extend from this view controller instead of UIViewController

Scriptable
  • 19,402
  • 5
  • 56
  • 72