如何创建无xib文件的纯代码Cocoa文档应用
纯代码将普通应用转为文档应用(iOS/macOS)
文档应用的核心是基于UIDocument(iOS)或NSDocument(macOS)的文档管理体系,纯代码转换需要完成项目配置、文档类实现、应用生命周期适配、内容视图开发这几个核心步骤,以下是具体操作:
1. 修改项目配置(Info.plist)
首先要在Info.plist中添加文档类型和UTI(统一类型标识符)配置,让系统识别你的应用支持的文档格式:
- 添加
CFBundleDocumentTypes数组,每个元素对应一种文档类型,示例字典配置:<dict> <key>CFBundleTypeName</key> <string>Text Document</string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>com.yourcompany.textdoc</string> </array> </dict> - 添加
UTExportedTypeDeclarations数组,定义自定义UTI,示例字典配置:<dict> <key>UTTypeIdentifier</key> <string>com.yourcompany.textdoc</string> <key>UTTypeDescription</key> <string>Custom Text Document</string> <key>UTTypeConformsTo</key> <array> <string>public.text</string> </array> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <array> <string>txtdoc</string> </array> </dict> </dict>
2. 实现核心文档类
创建继承自UIDocument或NSDocument的类,纯代码实现文档的读写逻辑:
iOS 示例
import UIKit class TextDocument: UIDocument { var content: String = "" // 从文件读取内容 override func load(fromContents contents: Any, ofType typeName: String?) throws { guard let data = contents as? Data else { return } content = String(data: data, encoding: .utf8) ?? "" } // 将内容写入文件 override func contents(forType typeName: String) throws -> Any { return content.data(using: .utf8) ?? Data() } }
macOS 示例
import Cocoa class TextDocument: NSDocument { var content: String = "" override func read(from data: Data, ofType typeName: String) throws { content = String(data: data, encoding: .utf8) ?? "" } override func data(ofType typeName: String) throws -> Data { return content.data(using: .utf8) ?? Data() } }
3. 适配应用生命周期,处理文档打开逻辑
根据平台不同,在应用入口(SceneDelegate/iOS 或 AppDelegate/macOS)中处理文档的创建、打开请求:
iOS(SceneDelegate 版本)
import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } let window = UIWindow(windowScene: windowScene) self.window = window // 处理启动时的文档请求(从外部打开文档或新建) if let url = connectionOptions.urlContexts.first?.url { openDocument(at: url, in: window) } else { createNewDocument(in: window) } window.makeKeyAndVisible() } // 处理外部打开文档的请求 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url, let window = window else { return } openDocument(at: url, in: window) } // 打开已有文档 private func openDocument(at url: URL, in window: UIWindow) { let document = TextDocument(fileURL: url) let contentVC = DocumentContentVC(document: document) window.rootViewController = UINavigationController(rootViewController: contentVC) document.open { success in if success { contentVC.updateContent() } else { // 处理打开失败逻辑 } } } // 创建新文档 private func createNewDocument(in window: UIWindow) { let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("NewDocument.txtdoc") let document = TextDocument(fileURL: tempURL) let contentVC = DocumentContentVC(document: document) window.rootViewController = UINavigationController(rootViewController: contentVC) document.save(to: tempURL, for: .forCreating) { success in if success { contentVC.updateContent() } else { // 处理创建失败逻辑 } } } }
macOS(AppDelegate 版本)
import Cocoa class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { // 启动时新建文档 NSDocumentController.shared.newDocument(self) } // 处理外部打开文档请求 func application(_ sender: NSApplication, openFile filename: String) -> Bool { let url = URL(fileURLWithPath: filename) return NSDocumentController.shared.openDocument(withContentsOf: url, display: true) { _, _, error in return error == nil } } }
4. 实现文档内容视图控制器
纯代码创建用于显示和编辑文档内容的视图控制器,并关联文档实例:
iOS 示例
import UIKit class DocumentContentVC: UIViewController { let document: TextDocument private let textView = UITextView() init(document: TextDocument) { self.document = document super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white setupTextView() updateContent() } private func setupTextView() { textView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(textView) NSLayoutConstraint.activate([ textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), textView.leadingAnchor.constraint(equalTo: view.leadingAnchor), textView.trailingAnchor.constraint(equalTo: view.trailingAnchor), textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) textView.delegate = self } func updateContent() { textView.text = document.content } } extension DocumentContentVC: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { document.content = textView.text // 标记文档有修改,触发自动保存 document.updateChangeCount(.done) } }
macOS 示例
import Cocoa class DocumentContentVC: NSViewController { let document: TextDocument private let textView = NSTextView() init(document: TextDocument) { self.document = document super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { view = NSView() view.wantsLayer = true view.layer?.backgroundColor = NSColor.white.cgColor } override func viewDidLoad() { super.viewDidLoad() setupTextView() updateContent() } private func setupTextView() { let scrollView = NSScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.documentView = textView view.addSubview(scrollView) NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.topAnchor), scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) textView.delegate = self } func updateContent() { textView.string = document.content } } extension DocumentContentVC: NSTextViewDelegate { func textDidChange(_ notification: Notification) { document.content = textView.string document.updateChangeCount(.changeDone) } }
5. 关联文档与视图控制器(macOS 额外步骤)
macOS 需要在文档类中指定视图控制器,纯代码实现:
extension TextDocument { override func makeWindowControllers() { let contentVC = DocumentContentVC(document: self) let windowController = NSWindowController(window: NSWindow(contentViewController: contentVC)) addWindowController(windowController) } }
内容的提问来源于stack exchange,提问作者vbezhenar
相关产品推荐
相关产品推荐

