mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
feat(apple): native SwiftUI app for iOS and macOS
Add a native SwiftUI VniDrop app (Send/Receive/Settings) talking to the Rust core via generated UniFFI Swift bindings, plus the uniffi-bindgen helper crate. iOS uses a TabView, macOS a NavigationSplitView sidebar.
This commit is contained in:
66
apple/VniDrop/UI/Components/AdaptiveDrawer.swift
Normal file
66
apple/VniDrop/UI/Components/AdaptiveDrawer.swift
Normal file
@@ -0,0 +1,66 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Presents modal content in a native sheet. On phones it uses medium/large
|
||||
/// detents with a grabber; on wider layouts the sheet is form-sized. Content is
|
||||
/// wrapped in a `NavigationStack` so it gets a native title bar + Close button.
|
||||
struct AdaptiveDrawer<DrawerContent: View>: ViewModifier {
|
||||
@Binding var isPresented: Bool
|
||||
let windowClass: WindowClass
|
||||
let onDismiss: () -> Void
|
||||
@ViewBuilder let drawerContent: () -> DrawerContent
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content.sheet(
|
||||
isPresented: Binding(get: { isPresented }, set: { if !$0 { onDismiss() } })
|
||||
) {
|
||||
SheetChrome(onClose: onDismiss) { drawerContent() }
|
||||
.modifier(PhoneDetents(enabled: windowClass == .phone))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PhoneDetents: ViewModifier {
|
||||
let enabled: Bool
|
||||
func body(content: Content) -> some View {
|
||||
if enabled {
|
||||
content
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationDragIndicator(.visible)
|
||||
} else {
|
||||
content.frame(minWidth: 460, minHeight: 480)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SheetChrome<Content: View>: View {
|
||||
let onClose: () -> Void
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView { content().padding(.top, 4) }
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(String(localized: "button_close"), action: onClose)
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func adaptiveDrawer<DrawerContent: View>(
|
||||
isPresented: Binding<Bool>,
|
||||
windowClass: WindowClass,
|
||||
onDismiss: @escaping () -> Void,
|
||||
@ViewBuilder content: @escaping () -> DrawerContent
|
||||
) -> some View {
|
||||
modifier(AdaptiveDrawer(
|
||||
isPresented: isPresented, windowClass: windowClass,
|
||||
onDismiss: onDismiss, drawerContent: content
|
||||
))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user