简体   繁体   中英

How to display incoming call view as top on SwiftUI?

I have tried to simulate how displaying the call view on the top of all. But I have some unclear points:

  • Is there any way to make it more simple?
  • Will the view hierarchy protected for that cases?

AppView.swift

import SwiftUI

struct AppView: View {
    
    @ObservedObject var callVM: CallViewModel
    
    init() {
        self.callVM = CallViewModel()
    }
    
    var body: some View {
        VStack {
            IncomingCallView(rootView: appView, isActive: self.$callVM.isIncomingCallActive)
            TabView {
                TabOne()
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text("Menu")
                    }

                TabTwo()
                    .tabItem {
                        Image(systemName: "square.and.pencil")
                        Text("Order")
                    }
            }
        }
        .onAppear(perform: load)
    }
    
    var appView: some View {
        Text("")
    }
    
    func load() {
        self.callVM.getCall()
    }
}

struct AppView_Previews: PreviewProvider {
    static var previews: some View {
        AppView()
    }
}

CallViewModel.swift

import Foundation

class CallViewModel: ObservableObject {
    
    @Published public var isIncomingCallActive = Bool()
    
    init() {
        self.isIncomingCallActive = false
    }
    
    func getCall() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.isIncomingCallActive = true
        }
    }
}

IncomingCallView.swift

import SwiftUI

struct IncomingCallView<RootView: View>: View {
    private let rootView: RootView
    @Binding var isActive: Bool

    init(rootView: RootView, isActive: Binding<Bool>) {
        self.rootView = rootView
        self._isActive = isActive
    }

    var body: some View {
        rootView
            .background(Activator(isActive: $isActive))
    }

    struct Activator: UIViewRepresentable {
        @Binding var isActive: Bool
        @State private var myWindow: UIWindow? = nil

        func makeUIView(context: Context) -> UIView {
            let view = UIView()
            DispatchQueue.main.async {
                self.myWindow = view.window
            }
            return view
        }

        func updateUIView(_ uiView: UIView, context: Context) {
            guard let holder = myWindow?.rootViewController?.view else { return }

            if isActive && context.coordinator.controller == nil {
                context.coordinator.controller = UIHostingController(rootView: loadingView)

                let view = context.coordinator.controller!.view
                view?.backgroundColor = UIColor.black.withAlphaComponent(1)
                view?.isUserInteractionEnabled = true
                view?.translatesAutoresizingMaskIntoConstraints = false
                holder.addSubview(view!)
                holder.isUserInteractionEnabled = true

                view?.leadingAnchor.constraint(equalTo: holder.leadingAnchor).isActive = true
                view?.trailingAnchor.constraint(equalTo: holder.trailingAnchor).isActive = true
                view?.topAnchor.constraint(equalTo: holder.topAnchor).isActive = true
                view?.bottomAnchor.constraint(equalTo: holder.bottomAnchor).isActive = true
            } else if !isActive {
                context.coordinator.controller?.view.removeFromSuperview()
                context.coordinator.controller = nil
                holder.isUserInteractionEnabled = true
            }
        }

        func makeCoordinator() -> Coordinator {
            Coordinator()
        }

        class Coordinator {
            var controller: UIViewController? = nil
        }
        
        private var loadingView: some View {
            HStack(spacing: 20) {
                
                Button(action: {
                    print("acceptCall pressed")
                    // change UI for accepted call
                }) {
                    Image("acceptCall")
                        .resizable()
                        .frame(width: 50, height: 50, alignment: .center)
                        .aspectRatio(contentMode: .fit)
                }

                Button(action: {
                    // close the view
                    print("rejectCall pressed")
                    self.isActive = false
                }) {
                    Image("rejectCall")
                        .resizable()
                        .frame(width: 50, height: 50, alignment: .center)
                        .aspectRatio(contentMode: .fit)
                }
                
            }
            .frame(width: 300, height: 300)
            .background(Color.primary.opacity(0.7))
            .cornerRadius(10)
        }
    }
}

Something in just swiftUI would look like this

struct AppView: View {
    
    @StateObject var callVM = CallViewModel()
    
    var body: some View {
        ZStack {
            TabView {
                Text("TabOne")
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text("Menu")
                    }
                
                Text("TabTwo")
                    .tabItem {
                        Image(systemName: "square.and.pencil")
                        Text("Order")
                    }
            }
            if callVM.isIncomingCallActive {
                ZStack {
                    Color.green
                    VStack {
                        Text("Incoming call...")
                        Button(action: { callVM.isIncomingCallActive = false }) {
                            Text("Cancel")
                        }
                    }
                }.edgesIgnoringSafeArea(.all) // <= here
            }
        }
        .onAppear {
            self.callVM.getCall()
        }
    }
}

class CallViewModel: ObservableObject {
    
    @Published public var isIncomingCallActive = false
    
    func getCall() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.isIncomingCallActive = true
        }
    }
}

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