2
respostas

[Dúvida] Desafio de criar o componente do botão

Consegui criar o botão, porém queria entender qual a melhor maneira de você adicionar o target. No meu caso eu fiz semelhante as aulas anteriores de criar um delegate do componente e implementar o delegate na view pai que chama o componente, porém fiquei na dúvida se isso seria o correto, já que não estou conseguindo fazer funcionar. Existe uma maneira melhor para esse caso?


protocol APButtonDelegate: AnyObject {
    func didButtonTap(_ sender: APButton)
}

class APButton: UIView {
    
    var buttonText: String
    
    weak var delegate: APButtonDelegate?
    
    init(buttonText: String) {
        self.buttonText = buttonText
        
        super.init(frame: .zero)
        setupView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    lazy var button: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor(named: "ColorCoral")
        button.titleLabel?.font = .init(name: "Poppins-Bold", size: 18)
        button.layer.cornerRadius = 8
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)

        return button
    }()
    
    private func setupView(){
        updateUI()
        addSubviews()
        setupConstraints()
    }
    

    
    private func updateUI(){
        button.setTitle(buttonText, for: .normal)
    }
    
    private func addSubviews(){
        addSubview(button)
    }
    
    private func setupConstraints(){
        NSLayoutConstraint.activate([
            button.topAnchor.constraint(equalTo: topAnchor),
            button.leadingAnchor.constraint(equalTo: leadingAnchor),
            button.trailingAnchor.constraint(equalTo: trailingAnchor),
            button.heightAnchor.constraint(equalToConstant: 48),
        ])
    }
    
    @objc private func buttonTapped(){
        print("Clicou o botão")
        delegate?.didButtonTap(self)
    }
}

Criei o botão da seguinte forma e declarei assim no pai:

    private lazy var signInButton: APButton = {
        let buttonComponent = APButton(buttonText: "Entrar")
        buttonComponent.translatesAutoresizingMaskIntoConstraints = false
        buttonComponent.delegate = self
        
        return buttonComponent
    }()

//....... Constraints
NSLayoutConstraint.activate([
            shape2.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -48),
            
            logo.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            logo.topAnchor.constraint(equalTo: shape.bottomAnchor, constant: -108),
            
            text.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
            text.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32),
            text.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 32),
            
            paws.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32),
            stack.topAnchor.constraint(equalTo: text.bottomAnchor, constant: 32),
            
            emailTxtField.heightAnchor.constraint(equalToConstant: 48),
            passwordTxtField.heightAnchor.constraint(equalToConstant: 48),
            
            signInButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 88),
            signInButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -88),
            signInButton.topAnchor.constraint(equalTo: stack.bottomAnchor, constant: 32),
        ])

//............... Extension

extension SignInViewController: APButtonDelegate {
    func didButtonTap(_ sender: APButton) {
        signIn()
    }
}

Mesmo assim parece que ele não recebe o clique, não sei pq

2 respostas

Oi, Pedro! Como vai?

Pelo código que você enviou, a abordagem do delegate está correta para casos como este, já que ela mantém o código desacoplado e segue as boas práticas. No entanto, o problema pode estar relacionado a dois pontos importantes: o tamanho do botão e como ele está sendo adicionado à hierarquia da view. Você pode revisar e ajustar o seguinte:

  1. Verifique se o botão ocupa espaço na tela: Use um backgroundColor temporário no APButton para inspecionar se ele aparece visualmente.

    class APButton: UIView {
        // Dentro de setupView()
        self.backgroundColor = UIColor.red.withAlphaComponent(0.3)
    }
    
  2. Garanta que o toque não está sendo "consumido" por outra view acima. Adicione o método isUserInteractionEnabled = true na view pai ou no botão, se necessário.

  3. Ajuste o delegate para evitar referências circulares:

Exemplo ajustado do botão e sua funcionalidade:

protocol APButtonDelegate: AnyObject {
    func didButtonTap(_ sender: APButton)
}

class APButton: UIView {
    var buttonText: String
    weak var delegate: APButtonDelegate?

    init(buttonText: String) {
        self.buttonText = buttonText
        super.init(frame: .zero)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private lazy var button: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor(named: "ColorCoral")
        button.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
        button.layer.cornerRadius = 8
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        return button
    }()

    private func setupView() {
        button.setTitle(buttonText, for: .normal)
        addSubview(button)

        NSLayoutConstraint.activate([
            button.topAnchor.constraint(equalTo: topAnchor),
            button.leadingAnchor.constraint(equalTo: leadingAnchor),
            button.trailingAnchor.constraint(equalTo: trailingAnchor),
            button.heightAnchor.constraint(equalToConstant: 48),
        ])
    }

    @objc private func buttonTapped() {
        print("Botão clicado")
        delegate?.didButtonTap(self)
    }
}

No pai:

extension SignInViewController: APButtonDelegate {
    func didButtonTap(_ sender: APButton) {
        print("Botão foi clicado")
        // Sua ação
    }
}

private lazy var signInButton: APButton = {
    let button = APButton(buttonText: "Entrar")
    button.translatesAutoresizingMaskIntoConstraints = false
    button.delegate = self
    return button
}()

Este código cria uma estrutura com as dependências corretamente configuradas e evita problemas com áreas de toque do botão não reconhecidas.

Espero ter ajudado. Conte com o apoio do Fórum na sua jornada :)

Abraços e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓. Bons Estudos!

Então, testando parece que a UIView não estava direcionando o clique para o UIButton. Eu tinha colocado o background color mas não tinha alteração alguma. Daí a solução que achei foi essa:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return button.frame.contains(point)
}

Sobrescrever esse método para direcionar o clique para o botão. Essa seria a melhor solução?