Tusker/Packages/TuskerComponents/Sources/TuskerComponents/AsyncToggle.swift

90 lines
2.2 KiB
Swift
Raw Normal View History

//
2024-04-08 22:45:54 +00:00
// AsyncToggle.swift
// TuskerComponents
//
// Created by Shadowfacts on 4/7/24.
// Copyright © 2024 Shadowfacts. All rights reserved.
//
import SwiftUI
2024-04-08 22:45:54 +00:00
public struct AsyncToggle: View {
let titleKey: LocalizedStringKey
2024-04-15 13:49:42 +00:00
#if !os(visionOS)
@available(iOS, obsoleted: 16.0, message: "Switch to LabeledContent")
let labelHidden: Bool
2024-04-15 13:49:42 +00:00
#endif
@Binding var mode: Mode
let onChange: (Bool) async -> Bool
public init(_ titleKey: LocalizedStringKey, labelHidden: Bool = false, mode: Binding<Mode>, onChange: @escaping (Bool) async -> Bool) {
self.titleKey = titleKey
2024-04-15 13:49:42 +00:00
#if !os(visionOS)
self.labelHidden = labelHidden
2024-04-15 13:49:42 +00:00
#endif
self._mode = mode
self.onChange = onChange
}
2024-04-08 22:45:54 +00:00
public var body: some View {
2024-04-15 13:49:42 +00:00
#if os(visionOS)
LabeledContent(titleKey) {
toggleOrSpinner
}
#else
if #available(iOS 16.0, *) {
LabeledContent(titleKey) {
toggleOrSpinner
}
} else if labelHidden {
toggleOrSpinner
} else {
HStack {
Text(titleKey)
Spacer()
toggleOrSpinner
}
}
2024-04-15 13:49:42 +00:00
#endif
}
@ViewBuilder
private var toggleOrSpinner: some View {
ZStack {
Toggle(titleKey, isOn: Binding {
mode == .on
} set: { newValue in
mode = .loading
Task {
let operationCompleted = await onChange(newValue)
if operationCompleted {
mode = newValue ? .on : .off
} else {
mode = newValue ? .off : .on
}
}
})
.labelsHidden()
.opacity(mode == .loading ? 0 : 1)
if mode == .loading {
ProgressView()
}
}
}
2024-04-08 22:45:54 +00:00
public enum Mode {
case off
case loading
case on
}
}
#Preview {
2024-04-08 22:45:54 +00:00
@State var mode = AsyncToggle.Mode.on
return AsyncToggle("", mode: $mode) { _ in
try! await Task.sleep(nanoseconds: NSEC_PER_SEC)
return true
}
}