Is there a SwiftUI version of the UIKit UIButton.ButtonType.close?
Asked Answered
X

5

6

Right now, I am in the process of converting from UIKit to SwiftUI. In UIKit, there is a native Close, X-Styled Button - UIButton.ButtonType.close, like shown below:

enter image description here

I wanted to find the equivalent of this in SwiftUI, if built in to SwiftUI already. It is understandable if Apple hasn't gotten around to building/converting this yet. I see this also in the Apple Maps App, which I believe is built in SwiftUI, like shown below (on the bottom right corner of the panel):

enter image description here

If there isn't a built in button styling, how would one go about creating a View for this close button, which would adapt to light and dark mode? Thank you so much!

Edit: In UIKIt's Storyboard Editor, here is what I am looking for:

enter image description here

Xanthene answered 10/8, 2022 at 8:4 Comment(3)
Did you check SFSymbols?Gannie
Yes I have, it does not quite fit what I am looking for. Here is an image to help show what I mean: https://assets.cdn.awesomeplayer.tech/img/images/1676599524_08:20:48am-2022-08-10.pngXanthene
@koen, I was able to tinker around in SFSymbols a bit more to find something very similar - thank you so much!: https://assets.cdn.awesomeplayer.tech/img/images/1154950817_08:26:26am-2022-08-10.pngXanthene
H
8

SwiftUI does not use concept of "button type", instead you can construct it by yourself, like

Button(action: {}) {
    Image(systemName: "xmark.circle.fill")   // << base !!
        .resizable()
        .frame(width: 32, height: 32) // << for demo
        .foregroundColor(.gray)
}

*with any other modifiers as you wish

demo

Xcode 13.4 / iOS 15.5

Hueston answered 10/8, 2022 at 8:27 Comment(0)
S
7

You can embed a UIKit UIButton with UIViewRepresentable.

struct CloseButton: UIViewRepresentable {
    private let action: () -> Void
    
    init(action: @escaping () -> Void) {
        self.action = action
    }
    
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .close)
        
        button.setContentCompressionResistancePriority(.required, for: .horizontal)
        button.setContentCompressionResistancePriority(.required, for: .vertical)
        button.setContentHuggingPriority(.required, for: .horizontal)
        button.setContentHuggingPriority(.required, for: .vertical)
        
        button.addTarget(context.coordinator, action: #selector(Coordinator.perform), for: .primaryActionTriggered)
        return button
    }
    
    func updateUIView(_ uiView: UIButton, context: Context) {
        context.coordinator.action = action
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(action: action)
    }
    
    class Coordinator {
        var action: () -> Void
        
        init(action: @escaping () -> Void) {
            self.action = action
        }
        
        @objc func perform() {
            action()
        }
    }
}

and then

CloseButton {
    // dismiss()
}

Example:

SomeView()
    .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
            CloseButton {
                dismiss()
            }
            .disabled(isBusy) // SwiftUI modifiers work fine.
        }
    }
Surprisal answered 7/9, 2022 at 2:41 Comment(1)
You probably want to set the content hugging and content compression resistance priorities to required as to prevent button stretching.Jada
F
5

It is not currently possible to use system button types in SwiftUI including the close button. I’ve submitted FB10380135 to request this addition.

In the meantime, you could make a Button look like the system close button. Mike Stern, an Apple Design Evangelist, noted in an Ask Apple Q&A:

The modal close button uses the "xmark" SF Symbol at 15pt bold (regular size symbol, not large or small variant) and a 30×30pt circle. The hit area is likely bigger as we typically go for at target size that's at least 44×44pt.

Also note the close button doesn’t change size as the dynamic type size increases/decreases. Do be sure to make its accessibility label “close” and add support for the large content viewer so people using accessibility text sizes can tap and hold the button to reveal a large xmark symbol with the word Close underneath.

I personally would recommend utilizing UIViewRepresentable to add a real close UIButton button though as opposed to trying to replicate it exactly - see the answer provided by MMP0.

Forgotten answered 9/1, 2023 at 4:20 Comment(0)
S
3

You can use the xmark.circle.fill symbol as the system image:

XMARK

⚠️ Note: there is a difference between xmark.circle.fill and x.circle.fill

Selfanalysis answered 10/8, 2022 at 8:29 Comment(0)
T
0

A condensed version of the UIViewRepresentable approach.

struct CloseButton: UIViewRepresentable {
    private let action: () -> Void
    init(action: @escaping () -> Void) { self.action = action }
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .close, primaryAction: UIAction { _ in action() })
        for axis in [NSLayoutConstraint.Axis.horizontal, .vertical] {
            button.setContentCompressionResistancePriority(.required, for: axis)
            button.setContentHuggingPriority(.required, for: axis)
        }
        return button
    }
    func updateUIView(_ uiView: UIButton, context: Context) {}
}
Thump answered 28/8, 2024 at 22:32 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.