forked from shadowfacts/Tusker
114 lines
3.5 KiB
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"),
|
|
])
|
|
}
|
|
}
|