Лучший способ добавить подвиды более одного UIViewControllers


Я пытаюсь найти хороший способ добавить простой UIButton subview с действием к некоторым UIViewControllers.

Я думаю, что он может работать с протоколом и его расширением, но расширение не позволяет методы @objc, это означает, что я не могу добавить цель с селектором.

Я создал другой класс для решения этой проблемы @objc, но не смог добавить представление в качестве параметра...

Я также создал подкласс UIViewController, и он отлично работает, но я не уверен что это хорошая идея, чтобы реализовать много UIViewControllers с этим подклассом.

Итак, вопрос в том, что является лучшим решением для добавления вложенных представлений с действием к более чем одному UIViewController?

Смотрите мой код ниже:

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 2

3 ответа:

Вы можете сделать расширение для UIButton и добавить метод со следующей семантикой:

func addTergetClosure(didTouch: () -> Void)

didTouch будет вызвано на touchUpInside событие, подробности здесь , как вы можете это сделать. Далее, вам просто нужно сделать протокол с самоограничением для UIViewController, как вы это сделали,который добавит вашу кнопку в качестве подвида. И я бы добавил свойство button как часть протокола, потому что вам, возможно, потребуется получить доступ к нему где-то в этих viewcontrollers, а не только добавить кнопку и закрытие, которые будут вызваны на touchUpInside.

Так и будет:

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

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

Если ваши кнопки выглядят одинаково над ViewControllers, чем я бы рекомендовал вам отделить эту логику для расширения UIButton со статическими полями, которые вернут UIButton с нужными вам стилями. В предоставленном решении вы просто установите кнопку в ViewController, как

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

extension UIButton {
    static let shadowed: UIButton = { ... }
}

Если вы хотите добавить более одного UIViewControllers в качестве подвида в один и тот же ViewController, то определенно вам нужно использовать контейнерное представление.

Если вы хотите, чтобы несколько контроллеров вида имели общую функциональность, или в вашем конкретном случае всегда добавляйте эту кнопку и в том же месте, вы должны создать базовый контроллер вида для их наследования.

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)

    }

}

Теперь вам не нужно ничего вызывать в каждом контроллере вида, просто расширяйте из этого контроллера вида вместо UIViewController