简体   繁体   中英

SwiftUI popover on iPad moves on device rotation

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)"))
    }
}

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.

在此处输入图片说明

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM