Добавить новое окно и сделать его ключевым окном в SWIFTUI
Я хочу добавить новое окно, так как хочу создать полноэкранный загрузчик. Я попытался добавить новое окно и установить его как rootviewcontroller. Но это не добавление в иерархию окон. Ниже мой код. Я изучаю swiftUI. Любая помощь приветствуется.
let window = UIWindow()
window.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
window.backgroundColor = .blue
window.isHidden = false
window.rootViewController = UIHostingController(rootView: Text("Loading...."))
window.makeKeyAndVisible()
2 ответа
Вам нужно обернуть UIActivityIndicator и сделать его UIViewRepresentable.
struct ActivityIndicator: UIViewRepresentable {
@Binding var isAnimating: Bool
style: UIActivityIndicatorView.Style
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
return UIActivityIndicatorView(style: style)
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
}
}
Затем вы можете использовать его следующим образом - вот пример наложения загрузки.
Примечание. Я предпочитаю использовать ZStack, а не overlay(:_), поэтому я точно знаю, что происходит в моей структуре реализации. LoadingView: View where Content: View {
@Binding var isShowing: Bool
var content: () -> Content
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
self.content()
.disabled(self.isShowing)
.blur(radius: self.isShowing ? 3 : 0)
VStack {
Text("Loading...")
ActivityIndicator(isAnimating: .constant(true), style: .large)
}
.frame(width: geometry.size.width / 2,
height: geometry.size.height / 5)
.background(Color.secondary.colorInvert())
.foregroundColor(Color.primary)
.cornerRadius(20)
.opacity(self.isShowing ? 1 : 0)
}
}
}
}
Если вы хотите показать другое окно, вам необходимо подключить новое UIWindow
к существующей оконной сцене, поэтому вот демонстрация возможного подхода, чтобы сделать это в SceneDelegate
, на основе опубликованных уведомлений.
// notification names declarations
let showFullScreenLoader = NSNotification.Name("showFullScreenLoader")
let hideFullScreenLoader = NSNotification.Name("hideFullScreenLoader")
// demo alternate window
struct FullScreenLoader: View {
var body: some View {
VStack {
Spacer()
Button("Close Loader") {
NotificationCenter.default.post(name: hideFullScreenLoader, object: nil)
}
}
}
}
// demo main window
struct MainView: View {
var body: some View {
VStack {
Button("Show Loader") {
NotificationCenter.default.post(name: showFullScreenLoader, object: nil)
}
Spacer()
}
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow? // << main window
var loaderWindow: UIWindow? // << alternate window
private var subscribers = Set<AnyCancellable>()
func makeAntherWindow() { // << alternate window creation
if let windowScene = window?.windowScene {
let newWindow = UIWindow(windowScene: windowScene)
let contentView = FullScreenLoader()
newWindow.rootViewController = UIHostingController(rootView: contentView)
self.loaderWindow = newWindow
newWindow.makeKeyAndVisible()
}
}
override init() {
super.init()
NotificationCenter.default.publisher(for: hideFullScreenLoader)
.sink(receiveValue: { _ in
self.loaderWindow = nil // remove alternate window
})
.store(in: &self.subscribers)
NotificationCenter.default.publisher(for: showFullScreenLoader)
.sink(receiveValue: { _ in
self.makeAntherWindow() // create alternate window
})
.store(in: &self.subscribers)
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = MainView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
...