iOS自定义来电/去电界面替换系统原生界面的实现方案咨询
First off, yes—it is possible to replace or overlay the default incoming/outgoing call screen on iOS, but the approach varies a lot depending on the iOS version you're targeting, and there are strict platform rules you can't ignore. Let's break this down clearly:
Key Feasibility Notes
- For iOS 14 and later: Apple introduced official, App Store-friendly APIs via
CallKit—this is the only supported way to build a custom call interface without risking rejection. - For iOS versions before 14: You could hack together a floating overlay using
UIWindowtricks, but this was unreliable, broke easily with OS updates, and was likely to get your app rejected. We'll focus on the supported iOS 14+ approach here.
Step-by-Step Implementation (iOS 14+)
1. Add the CallKit Framework
First, add the CallKit framework to your Xcode project. This is the core tool that lets your app interact with the system's call handling.
2. Implement the CXProviderDelegate
You'll need to conform to CXProviderDelegate to handle call events and trigger your custom UI:
import CallKit class CallManager: NSObject, CXProviderDelegate { private let provider: CXProvider override init() { let config = CXProviderConfiguration(localizedName: "Your App Name") config.supportsVideo = true // Toggle based on your app's call type config.maximumCallGroups = 1 config.maximumCallsPerCallGroup = 1 provider = CXProvider(configuration: config) super.init() provider.setDelegate(self, queue: nil) } // MARK: - CXProviderDelegate Methods func provider(_ provider: CXProvider, perform action: CXStartCallAction) { // Handle outgoing call initialization logic here action.fulfill() } func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { // Handle incoming call answer logic here action.fulfill() } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { // Configure audio routing (speaker/earpiece) for the call } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { // Clean up audio session resources after the call ends } }
3. Trigger Calls & Custom UI
Use CXCallController to tell the system to initiate or receive a call, which will trigger your custom interface:
For Outgoing Calls:
let callController = CXCallController() let recipientHandle = CXHandle(type: .generic, value: "Recipient Name/Number") let startCallAction = CXStartCallAction(call: UUID(), handle: recipientHandle) startCallAction.isVideo = false // Adjust for video calls let transaction = CXTransaction(action: startCallAction) callController.request(transaction) { error in if let error = error { print("Failed to start call: \(error.localizedDescription)") } else { // Your custom UI will be triggered next } }
For Incoming Calls:
let callUpdate = CXCallUpdate() callUpdate.remoteHandle = CXHandle(type: .generic, value: "Caller Name/Number") callUpdate.hasVideo = false callUpdate.supportsDTMF = true callUpdate.supportsHolding = true provider.reportNewIncomingCall(with: UUID(), update: callUpdate, completion: { error in if let error = error { print("Failed to report incoming call: \(error.localizedDescription)") } else { // System will prompt your custom call UI } })
4. Build & Present Your Custom Call UI
Create a UIViewController for your custom call screen, then present it when the call starts using this delegate method:
func provider(_ provider: CXProvider, didBegin call: CXCall) { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } let customCallVC = CustomCallViewController() // Your custom call screen VC customCallVC.modalPresentationStyle = .fullScreen windowScene.windows.first?.rootViewController?.present(customCallVC, animated: true) }
Critical Limitations to Keep in Mind
- App Store Rules: You can only use CallKit for actual voice/video calls (VoIP, SIP, etc.). Using it for fake calls or non-call features will get your app rejected.
- System Overrides: iOS will still show lock-screen call notifications and let users answer/reject calls from there—you can't fully disable system-level call controls.
- Background Handling: For incoming calls when the app is closed or in the background, you'll need to set up VoIP push notifications to trigger the custom UI.
内容的提问来源于stack exchange,提问作者Wasim Mirza

