-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
27d3c64
commit 7efbe95
Showing
1 changed file
with
99 additions
and
0 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
Sources/BSWInterfaceKit/SwiftUI/Extensions/SwiftUI+AlwaysPopover.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import SwiftUI | ||
|
||
extension View { | ||
func alwaysPopover<Content: View, Value: Identifiable>(id: Value.ID, isPresented: Binding<Value?>, @ViewBuilder content: @escaping () -> Content) -> some View { | ||
self.modifier(AlwaysPopoverModifier(id: id, isPresented: isPresented, contentBlock: content)) | ||
} | ||
|
||
} | ||
|
||
private struct AlwaysPopoverModifier<PopoverContent: View, Value: Identifiable>: ViewModifier { | ||
|
||
let id: Value.ID | ||
let isPresented: Binding<Value?> | ||
let contentBlock: () -> PopoverContent | ||
@State private var anchorView = UIView() | ||
|
||
func body(content: Content) -> some View { | ||
if let value = isPresented.wrappedValue, value.id == id { | ||
presentPopover(value: value) | ||
} | ||
|
||
return content | ||
.background(InternalAnchorView(uiView: anchorView)) | ||
} | ||
|
||
func presentPopover(value: Value) { | ||
let contentController = ContentViewController( | ||
// Ensures the text wraps to fit the content | ||
rootView: contentBlock() | ||
.fixedSize(horizontal: false, vertical: true), | ||
isPresented: isPresented | ||
) | ||
contentController.modalPresentationStyle = .popover | ||
|
||
let view = anchorView | ||
guard let popover = contentController.popoverPresentationController else { return } | ||
popover.sourceView = view | ||
popover.sourceRect = view.bounds | ||
popover.delegate = contentController | ||
|
||
guard let sourceVC = view.next() as UIViewController? else { return } | ||
sourceVC.present(contentController, animated: true) | ||
} | ||
|
||
struct InternalAnchorView: UIViewRepresentable { | ||
let uiView: UIView | ||
|
||
func makeUIView(context: Self.Context) -> UIView { | ||
Check failure on line 48 in Sources/BSWInterfaceKit/SwiftUI/Extensions/SwiftUI+AlwaysPopover.swift GitHub Actions / build
|
||
uiView | ||
} | ||
|
||
func updateUIView(_ uiView: UIView, context: Self.Context) { } | ||
Check failure on line 52 in Sources/BSWInterfaceKit/SwiftUI/Extensions/SwiftUI+AlwaysPopover.swift GitHub Actions / build
|
||
} | ||
|
||
class ContentViewController<FinalView: View>: UIHostingController<FinalView>, UIPopoverPresentationControllerDelegate { | ||
|
||
var isPresented: Binding<Value?> | ||
|
||
init(rootView: FinalView, isPresented: Binding<Value?>) { | ||
self.isPresented = isPresented | ||
super.init(rootView: rootView) | ||
} | ||
|
||
@preconcurrency required dynamic init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
updatePreferredContentSize() | ||
} | ||
|
||
override func viewDidLayoutSubviews() { | ||
super.viewDidLayoutSubviews() | ||
updatePreferredContentSize() | ||
} | ||
|
||
private let MaxWidth: CGFloat = 320 | ||
|
||
private func updatePreferredContentSize() { | ||
let size = view.systemLayoutSizeFitting( | ||
CGSize(width: MaxWidth, height: UIView.layoutFittingCompressedSize.height), | ||
withHorizontalFittingPriority: .required, | ||
verticalFittingPriority: .fittingSizeLevel | ||
) | ||
preferredContentSize = size | ||
} | ||
|
||
// MARK: UIPopoverPresentationControllerDelegate | ||
|
||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { | ||
return .none | ||
} | ||
|
||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { | ||
self.isPresented.wrappedValue = nil | ||
} | ||
} | ||
} |