将SwiftUI视图覆盖到包括工作表在内的所有其他视图上

时间:2019-12-11 00:05:31

标签: ios swift swiftui

我已经看到有关UIKit的讨论,但最近没有包含SwiftUI的讨论。

我有一个Master-Detail样式的应用程序,该应用程序要求两个浮动按钮在应用程序中始终可见。

“设置”按钮:轻按该按钮时,将通过一些切换取消隐藏另一个叠加层 添加记录按钮:点击时将通过@State变量显示工作表,再次点击时将关闭工作表。

如果我将按钮设置为NavigationView上的叠加层,则在显示工作表时会将其推入背景。这并不特别令人惊讶,但并不是设计中要求的行为。

第一种方法-在NavigationView()上叠加。

struct ContentView: View {
    @State var addRecordPresented: Bool = false
      var body: some View {

        NavigationView {
            VStack {
                SomeView()
                AnotherView()
                    .sheet(isPresented: $addRecordPresented, onDismiss: {self.addRecordPresented = false}) {AddRecordView()}
            }
            .overlay(NewRecordButton(isOn: $addRecordPresented).onTapGesture{self.addRecordPresented.toggle()}, alignment: .bottomTrailing)
        }
    }
}

第二种方法-覆盖为第二个UIWindow

然后我再次开始,尝试在SceneDelegate中创建第二个UIWindow,其中包含一个ViewController,该ViewController承载UIHostingController中的SwiftUI视图,但是我没有成功尝试覆盖以允许两个按钮都可被点击,但是对于其他点击传递到覆盖窗口后面的窗口。

已删除数据流内容,这只是试图呈现一个可轻击的圆形,在轻击时将在绿色和红色之间切换,并且在主内容视图中具有紫色正方形,在轻击时将显示黄色页面。圆正确地漂浮在图纸的顶部,但是从不响应丝锥。

在SceneDelegate中:

    var window: UIWindow?
    var wimdow2: UIWindow2?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

           (...)


        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window

            window.windowLevel = .normal
            window.makeKeyAndVisible()

            let window2 = UIWindow2(windowScene: windowScene)
            window2.rootViewController = OverlayViewController()

            self.window2 = window2
            window2.windowLevel = .normal+1
            window2.isHidden = false
        }
    }


            (...)


        class UIWindow2: UIWindow {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        let hitView = super.hitTest(point, with: event)

        if hitView != self {
            return nil
        }
        return hitView
    }
}

在ViewController文件中:

import UIKit
import SwiftUI

class OverlayViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()


        let overlayView = UIHostingController(rootView: NewRecordButton())
        addChild(overlayView)

        overlayView.view.backgroundColor = UIColor.clear
        overlayView.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        overlayView.view.isUserInteractionEnabled = true

        self.view.addSubview(overlayView.view)
        overlayView.didMove(toParent: self)

    }

}

struct NewRecordButton: View {
    @State var color = false
    var body: some View {
        Circle().foregroundColor(color ? .green : .red).frame(width: 50, height: 50).onTapGesture {
            self.color.toggle()
            print("tapped circle")
        }
    }
}

在主内容窗口中显示纯香草swiftUI视图:

import SwiftUI

struct ContentView: View {
    @State var show: Bool = false
    var body: some View {

        NavigationView {
            VStack {
                Rectangle().frame(width: 100, height: 100).foregroundColor(.purple).onTapGesture {self.show.toggle()}
                Text("Tap for Yellow").sheet(isPresented: $show, content: {Color.yellow}
                )
            }
        }
    }
}

对于如何正确实施此方法的任何建议或参考,将不胜感激!

1 个答案:

答案 0 :(得分:0)

从技术上讲这是可能的,但是我设法使其工作的方法脆弱而丑陋。 这个想法是

  1. 将按钮添加为应用程序键窗口的子级,然后
  2. 每次提交工作表时,先把它带到前面。 具体来说,在开始演示表格后,必须将其放在前面。

也就是说:

  1. UIApplication.shared.windows.first?.addSubview(settingsButton)
  2. 要呈现任何工作表时,通知settingsButton的所有者,以便将按钮带到前面:

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { UIApplication.shared.windows.first?.bringSubviewToFront(settingsButton) }