Tusker/Packages/TuskerComponents/Sources/TuskerComponents/MenuPicker.swift

114 lines
3.5 KiB
Swift

//
// MenuPicker.swift
// TuskerComponents
//
// Created by Shadowfacts on 11/7/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import SwiftUI
public struct MenuPicker<Value: Hashable>: UIViewRepresentable {
public typealias UIViewType = UIButton
@Binding var selection: Value
let options: [Option]
var buttonStyle: ButtonStyle
private var selectedOption: Option {
options.first(where: { $0.value == selection })!
}
public init(selection: Binding<Value>, options: [Option], buttonStyle: ButtonStyle = .labelAndIcon) {
self._selection = selection
self.options = options
self.buttonStyle = buttonStyle
}
public func makeUIView(context: Context) -> UIButton {
let button = UIButton(configuration: makeConfiguration())
button.showsMenuAsPrimaryAction = true
button.setContentHuggingPriority(.required, for: .horizontal)
return button
}
public func updateUIView(_ button: UIButton, context: Context) {
button.configuration = makeConfiguration()
button.menu = UIMenu(children: options.map { opt in
let action = UIAction(title: opt.title, subtitle: opt.subtitle, image: opt.image, state: opt.value == selection ? .on : .off) { _ in
selection = opt.value
}
action.accessibilityLabel = opt.accessibilityLabel
return action
})
button.accessibilityLabel = selectedOption.accessibilityLabel ?? selectedOption.title
button.isPointerInteractionEnabled = buttonStyle == .iconOnly
}
private func makeConfiguration() -> UIButton.Configuration {
var config = UIButton.Configuration.borderless()
if #available(iOS 16.0, *) {
config.indicator = .popup
}
if buttonStyle.hasIcon {
config.image = selectedOption.image
}
if buttonStyle.hasLabel {
config.title = selectedOption.title
}
#if targetEnvironment(macCatalyst)
config.macIdiomStyle = .bordered
#endif
return config
}
public struct Option {
public let value: Value
public let title: String
public let subtitle: String?
public let image: UIImage?
public let accessibilityLabel: String?
public init(value: Value, title: String, subtitle: String? = nil, image: UIImage? = nil, accessibilityLabel: String? = nil) {
self.value = value
self.title = title
self.subtitle = subtitle
self.image = image
self.accessibilityLabel = accessibilityLabel
}
}
public enum ButtonStyle {
case labelAndIcon, labelOnly, iconOnly
var hasLabel: Bool {
switch self {
case .labelAndIcon, .labelOnly:
return true
default:
return false
}
}
var hasIcon: Bool {
switch self {
case .labelAndIcon, .iconOnly:
return true
default:
return false
}
}
}
}
struct MenuPicker_Previews: PreviewProvider {
@State static var value = 0
static var previews: some View {
MenuPicker(selection: $value, options: [
.init(value: 0, title: "Zero"),
.init(value: 1, title: "One"),
.init(value: 2, title: "Two"),
])
}
}