0

I am trying to get a popover working on an iPad with swiftUI. The popover shows fine, however, when I rotate the device, the popover goes all over the place and does not anchor to its original place. Anybody a solution for this?

import SwiftUI

struct PopOver: View {
    var body: some View {
        Text("Hello world")
    }
}

struct ContentView: View {
    @State private var showPopover: Bool = false

    var body: some View {
        Button(action: {
            self.showPopover = true
        }) {
            Text("Select")
        }
        .popover(
            isPresented: self.$showPopover
        ) {
            PopOver()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().previewDevice(PreviewDevice(rawValue: "iPad Pro (10.5-inch)"))
    }
}

Dileep
  • 2,399
  • 4
  • 26
  • 39
iTukker
  • 2,083
  • 2
  • 16
  • 16
  • https://stackoverflow.com/questions/58179317/swiftui-sizing-a-popover-to-fit . Please see answers for this question – Dileep Mar 02 '20 at 07:29
  • Does this answer your question? [SwiftUI: Sizing a popover to fit](https://stackoverflow.com/questions/58179317/swiftui-sizing-a-popover-to-fit) – Glenn Posadas Mar 02 '20 at 07:42
  • I had a look at above suggestions, the problem is not the sizing, the problem is that on rotation the anchor point is not at the correct position anymore. I tried some solutions in above answers without any luck :( – iTukker Mar 02 '20 at 07:45

2 Answers2

1

Same as user3441734's answer, just more concise:

struct ContentView: View {
    @State var show = false
    var body: some View {
        Button("Select") { self.show.toggle() }
            .popover(isPresented: $show, content: {
                PopOver()
            })
            .hideOnRotate(show: $show)
    }
}

extension View {
    func hideOnRotate(show: Binding<Bool>) -> some View {
        onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification),
                  perform: { _ in show.wrappedValue = false })
    }
}

If you want to try to re-show as in the other answer, just add logic in the perform block.

chasew
  • 8,438
  • 7
  • 41
  • 48
0

Workaround

  1. dismiss popover on device rotation
  2. show popover again, if it was shown before rotation

    import SwiftUI
    
    
    import SwiftUI
    import UIKit
    import Combine
    
    struct PopOver: View {
        var body: some View {
            Text("Hello world")
        }
    }
    
    class Model: ObservableObject {
        @Published var show = false
        var handle: AnyCancellable?
        init() {
            handle = NotificationCenter.Publisher(center: .default, name: UIDevice.orientationDidChangeNotification).sink { (_) in
                print("orientation")
                if self.show {
                    self.show = false
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        self.show = true
                    }
                }
            }
        }
        deinit {
            handle?.cancel()
            handle = nil
        }
    }
    struct ContentView: View {
        @ObservedObject var popoverModel = Model()
        var body: some View {
            Button(action: {
                self.popoverModel.show.toggle()
            }) {
                Text("Select")
            }
            .popover(
                isPresented: self.$popoverModel.show
            ) {
                PopOver()
            }
        }
    }
    

    WARNING!

    if self.show {
        self.show = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.show = true
        }
    }
    

is very fragile, you better to find different way how to show popover not before the rotation transition finished.

enter image description here

user3441734
  • 16,722
  • 2
  • 40
  • 59