You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICloudSharingController在CloudKit应用中无法正常显示和工作问题求解

CloudKit共享功能适配问题:UICloudSharingController点击邮件/信息后界面异常

我一直在尝试让应用适配CloudKit实现记录共享功能,此前我已经开发过多个支持单用户多设备间Core Data记录同步的应用,但始终无法让UICloudSharingController正常实现记录共享功能。我可以正常弹出共享视图控制器,但点击邮件或信息选项后,仅显示键盘,无地址输入栏,也没有办法关闭该视图。这个问题困扰了我很久,所以我决定从基础的Apple官方"Sharing"示例应用开始调试,但该示例应用在我这边也无法正常运行。


相关代码实现

ContentView代码

import SwiftUI
import CloudKit

struct ContentView: View {

    @EnvironmentObject private var vm: ViewModel

    @State private var isAddingContact = false
    @State private var isSharing = false
    @State private var isProcessingShare = false

    @State private var showShareView = false
    @State private var showIntermediateView = false

    @State private var activeShare: CKShare?
    @State private var activeContainer: CKContainer?

    var body: some View {
        NavigationView {
            contentView
                .navigationTitle("Contacts")
                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        Button { Task.init { try await vm.refresh() } } label: { Image(systemName: "arrow.clockwise") }
                    }
                    ToolbarItem(placement: .navigationBarLeading) {
                        progressView
                    }
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button(action: { isAddingContact = true }) { Image(systemName: "plus") }
                    }
                }
        }
        .onAppear {
            Task.init {
                try await vm.initialize()
                try await vm.refresh()
            }
        }
        .sheet(isPresented: $isAddingContact, content: {
            AddContactView(onAdd: addContact, onCancel: { isAddingContact = false })
        })
    }

    /// This progress view will display when either the ViewModel is loading, or a share is processing.
    var progressView: some View {
        let showProgress: Bool = {
            if case .loading = vm.state {
                return true
            } else if isProcessingShare {
                return true
            }
            return false
        }()
        return Group {
            if showProgress {
                ProgressView()
            }
        }
    }

    /// Dynamic view built from ViewModel state.
    private var contentView: some View {
        Group {
            switch vm.state {
            case let .loaded(privateContacts, sharedContacts):
                List {
                    Section(header: Text("Private")) {
                        ForEach(privateContacts) { contactRowView(for: $0) }
                    }
                    Section(header: Text("Shared")) {
                        ForEach(sharedContacts) { contactRowView(for: $0, shareable: false) }
                    }
                }.listStyle(GroupedListStyle())
            
            case .error(let error):
                VStack {
                    Text("An error occurred: \(error.localizedDescription)").padding()
                    Spacer()
                }
            
            case .loading:
                VStack { EmptyView() }
            }
        }
    }

    /// Builds a `CloudSharingView` with state after processing a share.
    private func shareView() -> CloudSharingView? {

        guard let share = activeShare, let container = activeContainer else {
            return nil
        }
    
        return CloudSharingView(container: container, share: share)
    }

    /// Builds a Contact row view for display contact information in a List.
    private func contactRowView(for contact: Contact, shareable: Bool = true) -> some View {
        HStack {
            VStack(alignment: .leading) {
                Text(contact.name)
                Text(contact.phoneNumber)
                    .textContentType(.telephoneNumber)
                    .font(.footnote)
            }
        
            if shareable {
            
                Spacer()

                Button(action: { Task.init { try? await shareContact(contact) }
                }, label: { Image(systemName: "square.and.arrow.up") }).buttonStyle(BorderlessButtonStyle())
                    .sheet(isPresented: $isSharing, content: { shareView() })
            
            }//if sharable
        }//h
    }//contact row view

    // MARK: - Actions

    private func addContact(name: String, phoneNumber: String) async throws {
        try await vm.addContact(name: name, phoneNumber: phoneNumber)
        try await vm.refresh()
        isAddingContact = false
    }

    private func shareContact(_ contact: Contact) async throws {
        isProcessingShare = true
    
        do {
            let (share, container) = try await vm.createShare(contact: contact)
            isProcessingShare = false
            activeShare = share
            activeContainer = container
            isSharing = true
        } catch {
            debugPrint("Error sharing contact record: \(error)")
        }
    }
}

CloudSharingView(UICloudSharingController封装)代码

import Foundation
import SwiftUI
import UIKit
import CloudKit

/// This struct wraps a `UIImagePickerController` for use in SwiftUI.
struct CloudSharingView: UIViewControllerRepresentable {

    @Environment(\.presentationMode) var presentationMode
    let container: CKContainer
    let share: CKShare

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}

    func makeUIViewController(context: Context) -> some UIViewController {

        let sharingController = UICloudSharingController(share: share, container: container)
        sharingController.availablePermissions = [.allowReadWrite, .allowPrivate]
        sharingController.delegate = context.coordinator
    
        return sharingController
    }

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

    final class Coordinator: NSObject, UICloudSharingControllerDelegate {
    
        func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
            debugPrint("Error saving share: \(error)")
        }

        func itemTitle(for csc: UICloudSharingController) -> String? {
            "Sharing Example"
        }
    }
}

异常现象

  • 点击共享记录按钮后弹出的初始共享界面显示完全符合预期:
    共享初始界面
  • 点击信息/邮件选项后弹出的界面仅显示键盘,无地址输入栏,也无法关闭视图:
    共享跳转后异常界面

运行环境

  • Xcode 13.1
  • iOS 15
  • macOS Monterey

解决方案

这个问题是iOS 15低版本系统下,UICloudSharingController嵌套在SwiftUI的sheet中时,系统自带的分享跳转界面(邮件/信息视图)的层级渲染异常导致的,可通过以下两种方式修复:

方案1:改用UIKit的present方式弹出共享控制器

不要将UICloudSharingController封装为UIViewControllerRepresentable放在SwiftUI的sheet中,而是通过获取当前最顶层的UIViewController直接present:

// 新增工具方法获取当前顶层控制器
func getTopViewController() -> UIViewController? {
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
          let rootViewController = windowScene.windows.first?.rootViewController else {
        return nil
    }
    var topViewController = rootViewController
    while let presentedViewController = topViewController.presentedViewController {
        topViewController = presentedViewController
    }
    return topViewController
}

// 调整分享逻辑
private func shareContact(_ contact: Contact) async throws {
    isProcessingShare = true
    do {
        let (share, container) = try await vm.createShare(contact: contact)
        isProcessingShare = false
        // 直接弹出UICloudSharingController
        let sharingController = UICloudSharingController(share: share, container: container)
        sharingController.availablePermissions = [.allowReadWrite, .allowPrivate]
        sharingController.delegate = CloudSharingCoordinator.shared
        getTopViewController()?.present(sharingController, animated: true)
    } catch {
        debugPrint("Error sharing contact record: \(error)")
    }
}

// 单独抽离全局的协调器实现
class CloudSharingCoordinator: NSObject, UICloudSharingControllerDelegate {
    static let shared = CloudSharingCoordinator()
    
    func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
        debugPrint("Error saving share: \(error)")
    }

    func itemTitle(for csc: UICloudSharingController) -> String? {
        "Sharing Example"
    }
    
    func cloudSharingControllerDidStopSharing(_ csc: UICloudSharingController) {
        csc.dismiss(animated: true)
    }
}

方案2:升级开发环境

这个系统层级的bug在Xcode 14+、iOS 15.4+版本中已经被修复,直接升级开发环境和应用的编译基线也可解决该问题。

内容的提问来源于stack exchange,提问作者JohnSF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 06:27:03