我有一个要求,当触发任何alert 时,应该显示在应用程序的顶部视图.参考一些代码在互联网上的其他视图,我能够拿出解决方案,显示在视图或甚至表,如果它是顶部视图.

但是,alert 上的按钮或任何点击手势都不起作用.

注:我有一个非常复杂的应用程序,所以我创建了一个快速示例应用程序.如果你复制我的代码并运行它,应用程序将自动打开一个工作表,然后显示一个警告,以显示我在应用程序中执行的工作流,除了在我的应用程序表打开手动按钮单击.

100

import SwiftUI
import Foundation

struct ContentView: View {
    @StateObject var alertViewModel: AlertViewModel = AlertViewModel()
    @State private var showSheet: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack(alignment: .center) {
                Text("This is the main view. Opening sheet view...").padding(.top, 100.0)
                Spacer()
            }
            .onAppear {
                Task {
                    try await Task.sleep(nanoseconds: 3_000_000_000)
                    showSheet = true
                    try await Task.sleep(nanoseconds: 3_000_000_000)
                    alertViewModel.showAlert = true
                }
            }
            .environmentObject(alertViewModel)
            .sheet(isPresented: $showSheet, content: {
                SheetA()
            })
            .overlayModal(isPresented: $alertViewModel.showAlert, modalContent: alertViewModel.alertView)
        }
    }
}

struct SheetA: View {
    var body: some View {
        VStack {
            Text("Sheet A to demo alert on top of all views. Alert will popup soon...").padding(.top, 100.0)
            Spacer()
        }
    }
}

// MARK: CUSTOM ALERT CODE

class AlertViewModel: ObservableObject {
    @Published public var showAlert: Bool = false
    
    func alertView() -> some View {
        return CustomAlertView()
    }
}

struct CustomAlertView: View {
    @State private var alertHostingController: UIHostingController<AnyView>?
    
    @EnvironmentObject var alertViewModel: AlertViewModel
 
    var body: some View {
        ZStack {
            Color.gray.opacity(0.3).ignoresSafeArea()
            VStack {
                
            }.onAppear {
                showAlert()
            }
        }
    }
    
    @ViewBuilder
    func alertContent() -> some View {
        GeometryReader { geometry in
            VStack {
                Image(systemName: "info.circle").resizable().frame(width: 20, height: 20)
                    .padding(.top, 30).foregroundColor(.blue)

                Text("Hello World").foregroundColor(.black).font(.title2).bold().multilineTextAlignment(.center)
                    .padding([.leading, .trailing], 20.0).padding(.top, 12.0)
                Spacer()
                
                Text("This is an alert to show detailed message for the app.").foregroundColor(Color.secondary).font(.body).multilineTextAlignment(.center)
                    .padding([.leading, .trailing], 20.0).padding(.top, 12.0)
                    .onTapGesture {
                        print("Testing alert tap on message")
                    }
                
                Spacer()
                
                Button {
                    print("Button tapped")
                } label: {
                    Text("Click Me").buttonBorderShape(.roundedRectangle)
                }
                .padding(.bottom, 20.0)
            }
            .fixedSize(horizontal: false, vertical: true)
            .background(Color.gray.opacity(0.5))
            .cornerRadius(28)
            .clipped()
            .padding([.leading, .trailing], 5.0)
            .position(x: geometry.size.width/2, y: geometry.size.height/2)
            .frame(minWidth: 250, maxWidth: 350)
            //.allowsHitTesting(false)
        }
    }
    
    func showAlert() {
        let swiftUIView = alertContent()
        alertHostingController = UIHostingController(rootView: AnyView(swiftUIView))
        alertHostingController?.view.backgroundColor = .clear
        alertHostingController?.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width - 44.0, height: 0)

        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let window = windowScene.windows.first, let hostingController = alertHostingController {
            window.addSubview(hostingController.view)

            hostingController.view.center.x = window.center.x
            hostingController.view.center.y = window.center.y
        }
    }
    
    func dismissAlert() {
        UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut) {
            alertHostingController?.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: 0)
            alertHostingController?.view.alpha = 0
        }

        Task {
            await MainActor.run {
                alertHostingController?.view.removeFromSuperview()
                alertHostingController = nil
                alertViewModel.showAlert = false
            }
        }
    }
}

// MARK: OVERLAY CODE

struct OverlayView<OverlayContent: View>: ViewModifier {
    @Binding var isPresented: Bool

    var modalContent: OverlayContent

    func body(content: Content) -> some View {
        GeometryReader { _ in
            ZStack {
                content
                VStack {
                    if isPresented {
                        modalContent
                    } else {
                        Spacer()
                    }
                }
            }
        }
    }
}

extension View {
    @ViewBuilder
    func overlayModal<ModalContent: View>(isPresented: Binding<Bool>, @ViewBuilder modalContent: @escaping () -> ModalContent) -> some View {
        modifier(OverlayView(isPresented: isPresented, modalContent: modalContent()))
    }
}

你知道为什么我的敲击手势不起作用吗?欢迎任何其他解决方案,在所有视图之上呈现自定义alert .

推荐答案

您将视图的height设置为0:

        alertHostingController?.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width - 44.0, height: 0)

虽然它仍然显示,因为它没有剪切到边界,所以它没有注册任何用户输入.相反,您可能希望将视图的边缘固定到其超级视图.

func showAlert() {
    let swiftUIView = alertContent()
    let alertHostingController = UIHostingController(rootView: AnyView(swiftUIView))
    alertHostingController.view.backgroundColor = .clear

    if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
       let window = windowScene.windows.first {
        window.addSubview(alertHostingController.view)
        self.alertHostingController = alertHostingController // Store the controller if needed

        // Pin the view to the edges of the window
        alertHostingController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            alertHostingController.view.topAnchor.constraint(equalTo: window.topAnchor),
            alertHostingController.view.bottomAnchor.constraint(equalTo: window.bottomAnchor),
            alertHostingController.view.leadingAnchor.constraint(equalTo: window.leadingAnchor),
            alertHostingController.view.trailingAnchor.constraint(equalTo: window.trailingAnchor)
        ])
    }
}

Ios相关问答推荐

如何访问iOS模拟器中的zoom 设置?(在搜索中列出,但不可见)

SwiftUI标题/标题文本背景 colored颜色 屏幕宽度

Xcode 15中的版本控制发生了什么?

比较 `某个协议` 实例时出现编译错误

ITMS-90432:将应用上传到应用store 时出现问题

长按 SwiftUI 更改项目的顺序

一个 SwiftUI 视图中的多个垂直滚动视图

如何识别 SwiftUI 中点击的按钮 ID 以使该按钮动画化?

搜索在 ios xamarin.forms 中不起作用

在 iPad 上删除列表的顶部和底部行

分析应用程序版本时出错 - Apple 提交到store

`Task` 在内部调用异步函数时阻塞主线程

为什么 Safari 在使用 iOS 6 设备进行远程调试时显示No Inspectable Applications?

通过 iOS 创建和导出动画 gif?

无论我将构建版本或应用程序版本更改为什么,都会出现 ITEMS-4238冗余二进制上传错误

如何将 iPhone OSStatus 代码转换为有用的东西?

cocoapods中~>的用法是什么

如何使用完成按钮制作 UIPickerView?

从 Swift 转换为 Objective-c 的工具

zoom UIButton 动画 - Swift