5

I am making a SwiftUI Button from a custom UIButton class called UIPillButton. Here is the code for creating the SwiftUI button:

Button(action: {
    print("Button tapped")
}) {
    PillButton()
        .padding(.top)
}

Here is my class for creating a SwiftUI view called PillButton which converts my custom UIButton over:

struct PillButton: UIViewRepresentable {
    var ntPillButton = NTPillButton(type: .filled, title: "Start Test")
    
    func makeCoordinator() -> Coordinator { Coordinator(self) }
    
    class Coordinator: NSObject {
        var parent: PillButton
        
        init(_ pillButton: PillButton) {
            self.parent = pillButton
            super.init()
        }
    }

    func makeUIView(context: UIViewRepresentableContext<PillButton>) -> UIView {
        let view = UIView()
        view.addSubview(ntPillButton)
        
        NSLayoutConstraint.activate([
            ntPillButton.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            ntPillButton.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        
        return view
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PillButton>) {}
}

The issue is that the Button is not running the action if I tap on the PillButton itself. It only works if I select the buffer (padding) space above the PillButton. How can I make it so I can use the custom PillButton class as a normal SwiftUI button?

screenshot

Caleb Rudnicki
  • 345
  • 8
  • 17

1 Answers1

5

It is not clear what is NTPillButton, but if it is subclass of UIButton then the following demo of generic approach (using base UIButton) should clear and applicable.

Tested with Xcode 11.4 / iOS 13.4

The below gives this simple usage

    PillButton(title: "Start Test") {
        print("Button tapped")
    }
    .frame(maxWidth: .infinity)       // << screen-wide
    .padding(.top)

so now PillButton demo itself:

struct PillButton: UIViewRepresentable {
    let title: String
    let action: () -> ()

    var ntPillButton = UIButton()//NTPillButton(type: .filled, title: "Start Test")

    func makeCoordinator() -> Coordinator { Coordinator(self) }

    class Coordinator: NSObject {
        var parent: PillButton

        init(_ pillButton: PillButton) {
            self.parent = pillButton
            super.init()
        }

        @objc func doAction(_ sender: Any) {
            self.parent.action()
        }
    }

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle(self.title, for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.doAction(_ :)), for: .touchDown)
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {}
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Yes that works. However, my custom UIButton (NTPillButton) is made to only fit the size of the text so the button does not span the width of the screen like I aim for it to. Is there a way to pin the PillButton to the edges of the screen? Maybe with an HStack? – Caleb Rudnicki Jul 21 '20 at 20:45
  • @CalebRudnicki, just use .frame modifier. See updated. – Asperi Jul 22 '20 at 04:36
  • That's not quite it. The UIButton that the PillButton is inheriting from is only filling the amount of space it needs to fit the button text. Can i make the button span the whole width in swiftui code or will that have to be done in my UIButton's class? – Caleb Rudnicki Jul 22 '20 at 17:44