jellyflood/Swiftfin tvOS/App/PreferenceUIHosting/PreferenceUIHostingControll...

134 lines
3.9 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2023 Jellyfin & Jellyfin Contributors
//
import SwiftUI
import UIKit
// MARK: PreferenceUIHostingController
class PreferenceUIHostingController: UIHostingController<AnyView> {
init<V: View>(@ViewBuilder wrappedView: @escaping () -> V) {
let box = Box()
super.init(rootView: AnyView(
wrappedView()
.onPreferenceChange(ViewPreferenceKey.self) {
box.value?._viewPreference = $0
}
.onPreferenceChange(DidPressMenuPreferenceKey.self) {
box.value?.didPressMenuAction = $0
}
.onPreferenceChange(DidPressSelectPreferenceKey.self) {
box.value?.didPressSelectAction = $0
}
))
box.value = self
addButtonPressRecognizer(pressType: .menu, action: #selector(didPressMenuSelector))
addButtonPressRecognizer(pressType: .select, action: #selector(didPressSelectSelector))
}
@objc
dynamic required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.modalPresentationStyle = .fullScreen
}
private class Box {
weak var value: PreferenceUIHostingController?
init() {}
}
public var _viewPreference: UIUserInterfaceStyle = .unspecified {
didSet {
overrideUserInterfaceStyle = _viewPreference
}
}
var didPressMenuAction: ActionHolder = .init(action: {})
var didPressSelectAction: ActionHolder = .init(action: {})
private func addButtonPressRecognizer(pressType: UIPress.PressType, action: Selector) {
let pressRecognizer = UITapGestureRecognizer()
pressRecognizer.addTarget(self, action: action)
pressRecognizer.allowedPressTypes = [NSNumber(value: pressType.rawValue)]
view.addGestureRecognizer(pressRecognizer)
}
@objc
private func didPressMenuSelector() {
DispatchQueue.main.async {
self.didPressMenuAction.action()
}
}
@objc
private func didPressSelectSelector() {
DispatchQueue.main.async {
self.didPressSelectAction.action()
}
}
}
struct ActionHolder: Equatable {
static func == (lhs: ActionHolder, rhs: ActionHolder) -> Bool {
lhs.uuid == rhs.uuid
}
var action: () -> Void
let uuid = UUID().uuidString
}
// MARK: Preference Keys
struct ViewPreferenceKey: PreferenceKey {
typealias Value = UIUserInterfaceStyle
static var defaultValue: UIUserInterfaceStyle = .unspecified
static func reduce(value: inout UIUserInterfaceStyle, nextValue: () -> UIUserInterfaceStyle) {
value = nextValue()
}
}
struct DidPressMenuPreferenceKey: PreferenceKey {
static var defaultValue: ActionHolder = .init(action: {})
static func reduce(value: inout ActionHolder, nextValue: () -> ActionHolder) {
value = nextValue()
}
}
struct DidPressSelectPreferenceKey: PreferenceKey {
static var defaultValue: ActionHolder = .init(action: {})
static func reduce(value: inout ActionHolder, nextValue: () -> ActionHolder) {
value = nextValue()
}
}
// MARK: Preference Key View Extension
extension View {
func overrideViewPreference(_ viewPreference: UIUserInterfaceStyle) -> some View {
preference(key: ViewPreferenceKey.self, value: viewPreference)
}
func onMenuPressed(_ action: @escaping () -> Void) -> some View {
preference(key: DidPressMenuPreferenceKey.self, value: ActionHolder(action: action))
}
func onSelectPressed(_ action: @escaping () -> Void) -> some View {
preference(key: DidPressSelectPreferenceKey.self, value: ActionHolder(action: action))
}
}