I used complete code from this web site to create customized SwiftUI popover.
I wished to check it, so I created customized mannequin:
struct Mannequin {
var someInit = 0
}
After which created ContentView:
struct ContentView: View {
@State non-public var mannequin: Mannequin = .init()
@State non-public var bool: Bool = false
var physique: some View {
Button("Press me") {
bool.toggle()
}
.alwaysPopover(isPresented: $bool) {
Picker("", choice: $mannequin.someInit){
ForEach(1...30, id: .self){
Textual content("($0)")
.tag($0)
}
}
.pickerStyle(.wheel)
}
}
}
And code for customized popover is
non-public extension UIResponder {
func closestVC() -> UIViewController? {
var responder: UIResponder? = self
whereas responder != nil {
if let controller = responder as? UIViewController {
return controller
}
responder = responder?.subsequent
}
return nil
}
}
non-public struct InternalAnchorView: UIViewRepresentable {
typealias UIViewType = UIView
let uiView: UIView
func makeUIView(context: Self.Context) -> Self.UIViewType {
uiView
}
func updateUIView(_ uiView: Self.UIViewType, context: Self.Context) { }
}
extension View {
public func alwaysPopover<Content material>(isPresented: Binding<Bool>, @ViewBuilder content material: @escaping () -> Content material) -> some View the place Content material : View {
self.modifier(AlwaysPopoverModifier(isPresented: isPresented, contentBlock: content material))
}
}
struct AlwaysPopoverModifier<PopoverContent>: ViewModifier the place PopoverContent: View {
// Workaround for the lacking `@StateObject` in iOS 13.
non-public struct Retailer {
var anchorView = UIView()
}
@State non-public var retailer = Retailer()
func physique(content material: Content material) -> some View {
if isPresented.wrappedValue {
presentPopover()
}
return content material
.background(InternalAnchorView(uiView: retailer.anchorView))
}
let isPresented: Binding<Bool>
let contentBlock: () -> PopoverContent
non-public func presentPopover() {
let contentController = ContentViewController(rootView: contentBlock(), isPresented: isPresented)
contentController.modalPresentationStyle = .popover
let view = retailer.anchorView
guard let popover = contentController.popoverPresentationController else { return }
popover.sourceView = view
popover.sourceRect = view.bounds
popover.delegate = contentController
guard let sourceVC = view.closestViewControllerResponder() else { return }
if let presentedVC = sourceVC.presentedViewController {
presentedVC.dismiss(animated: true) {
sourceVC.current(contentController, animated: true)
}
} else {
sourceVC.current(contentController, animated: true)
}
}
}
class ContentViewController<V>: UIHostingController<V>, UIPopoverPresentationControllerDelegate the place V:View {
var isPresented: Binding<Bool>
init(rootView: V, isPresented: Binding<Bool>) {
self.isPresented = isPresented
tremendous.init(rootView: rootView)
}
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been applied")
}
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
self.isPresented.wrappedValue = false
}
override func viewDidLoad() {
tremendous.viewDidLoad()
let measurement = sizeThatFits(in: UIView.layoutFittingExpandedSize)
preferredContentSize = measurement
}
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}
However I observed that, earlier than dismissing popover, values from ForEach are reseted to preliminary worth earlier than dismissing popover.
I attempted to make use of .onChange
and monitor worth altering and utilizing .id
, however unsuccessfully.