PopoverLink.swift 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. //
  2. // PopoverLink.swift
  3. //
  4. // Created by Manuel Weiel on 09.07.20.
  5. //
  6. // From https://manuel.weiel.eu/bringing-the-simplicity-of-navigationlink-to-popovers/
  7. import SwiftUI
  8. public struct PopoverLink<Label, Destination> : View where Label : View, Destination : View {
  9. @Environment(\.horizontalSizeClass) var horizontalSizeClass
  10. private let destination: Destination
  11. private let label: Label
  12. private var isActive: Binding<Bool>?
  13. private var isFullScreen: Bool = false
  14. @State private var internalIsActive = false
  15. public init(_ localizedText: Text, destination: Destination) where Label == Text {
  16. self.init(destination: destination, label: { localizedText })
  17. }
  18. /// Creates an instance that presents `destination`.
  19. public init(destination: Destination, @ViewBuilder label: () -> Label) {
  20. self.destination = destination
  21. self.label = label()
  22. }
  23. /// Creates an instance that presents `destination` when active.
  24. public init(destination: Destination, isActive: Binding<Bool>, @ViewBuilder label: () -> Label) {
  25. self.destination = destination
  26. self.label = label()
  27. self.isActive = isActive
  28. }
  29. private init(_ other: PopoverLink, isFullScreen: Bool) {
  30. self.destination = other.destination
  31. self.isActive = other.isActive
  32. self.label = other.label
  33. self.isFullScreen = isFullScreen
  34. }
  35. private func popoverButton() -> some View {
  36. Button {
  37. (isActive ?? _internalIsActive.projectedValue).wrappedValue = true
  38. } label: {
  39. label
  40. }
  41. }
  42. /// The content and behavior of the view.
  43. public var body: some View {
  44. if isFullScreen {
  45. popoverButton().fullScreenCover(isPresented: (isActive ?? _internalIsActive.projectedValue)) {
  46. destination
  47. }
  48. } else {
  49. if horizontalSizeClass == .compact {
  50. popoverButton().sheet(isPresented: (isActive ?? _internalIsActive.projectedValue)) {
  51. destination
  52. }
  53. } else {
  54. popoverButton().popover(isPresented: (isActive ?? _internalIsActive.projectedValue)) {
  55. destination
  56. }
  57. }
  58. }
  59. }
  60. }
  61. extension PopoverLink {
  62. public func fullScreen() -> PopoverLink {
  63. PopoverLink(self, isFullScreen: true)
  64. }
  65. }