如何在Swift中为macOS打印功能添加页眉与页脚?
可行解决方案
方案一:使用CSS @page 规则(推荐,无需修改打印逻辑)
直接在HTML内容中嵌入打印专用CSS样式,利用CSS的@page规则和伪元素定义页眉、页脚,WKWebView完全支持这套标准打印样式。
实现步骤:
- 在HTML字符串头部添加打印样式:
<style media="print"> /* 设置页面边距,为页眉页脚预留空间 */ @page { margin-top: 60px; margin-bottom: 60px; margin-left: 40px; margin-right: 40px; } /* 页眉:显示文档标题 */ header { position: fixed; top: 0; left: 0; right: 0; height: 40px; text-align: center; font-size: 14px; font-weight: bold; } /* 页脚:显示页码与总页数 */ footer { position: fixed; bottom: 0; left: 0; right: 0; height: 40px; text-align: center; font-size: 12px; } /* 用CSS计数器实现自动页码 */ footer::after { content: "第 " counter(page) " 页,共 " counter(pages) " 页"; } /* 避免正文被页眉页脚遮挡 */ body { margin-top: 0; margin-bottom: 0; } </style>
- 在HTML的
<body>中添加页眉、页脚标签:
<header>你的文档标题</header> <!-- 原有HTML内容 --> <footer></footer>
- 替换代码中不可靠的延迟等待,改用
WKNavigationDelegate监听页面加载完成:
修改HTMLPrintView的实现:
public class HTMLPrintView: NSView { var webView: WKWebView public override init(frame frameRect: NSRect) { webView = WKWebView(frame: frameRect) super.init(frame: frameRect) addSubview(webView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func printView(htmlContent: String, window parentWindow: NSWindow) { let webView = WKWebView(frame: .zero) webView.navigationDelegate = self webView.loadHTMLString(htmlContent, baseURL: nil) } } // 扩展实现WKNavigationDelegate extension HTMLPrintView: WKNavigationDelegate { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { let printInfo = NSPrintInfo() printInfo.horizontalPagination = .fit printInfo.verticalPagination = .fit printInfo.isVerticallyCentered = true printInfo.isHorizontallyCentered = true let printOperation = webView.printOperation(with: printInfo) printOperation.printPanel.options.insert(.showsPaperSize) printOperation.printPanel.options.insert(.showsOrientation) printOperation.printPanel.options.insert(.showsPreview) printOperation.runModal(for: parentWindow, delegate: self, didRun: nil, contextInfo: nil) } }
方案二:自定义打印视图(原生NSView绘制)
如果CSS方案无法满足复杂样式需求,可以自定义打印视图,将WKWebView作为子视图,在父视图中重写打印方法绘制页眉页脚。
实现步骤:
- 创建自定义打印视图子类:
class CustomPrintView: NSView { var webView: WKWebView! var documentTitle: String = "" init(frame: NSRect, webView: WKWebView, title: String) { self.webView = webView self.documentTitle = title super.init(frame: frame) addSubview(webView) // 为页眉页脚预留空间,调整webView位置 webView.frame = bounds.inset(by: NSEdgeInsets(top: 60, left: 40, bottom: 60, right: 40)) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 重写绘制页面边框方法,添加页眉页脚 override func drawPageBorder(with rect: NSRect, in context: NSGraphicsContext) { super.drawPageBorder(with: rect, in: context) let cgContext = context.cgContext // 绘制页眉标题 let headerRect = NSRect(x: rect.origin.x, y: rect.origin.y + rect.size.height - 50, width: rect.size.width, height: 30) let headerAttrs: [NSAttributedString.Key: Any] = [ .font: NSFont.boldSystemFont(ofSize: 14), .foregroundColor: NSColor.black ] NSAttributedString(string: documentTitle, attributes: headerAttrs).draw(in: headerRect) // 绘制页脚页码 let pageNumber = cgContext.currentPage + 1 let totalPages = cgContext.totalPages let footerText = "第 \(pageNumber) 页,共 \(totalPages) 页" let footerAttrs: [NSAttributedString.Key: Any] = [ .font: NSFont.systemFont(ofSize: 12), .foregroundColor: NSColor.black ] let footerString = NSAttributedString(string: footerText, attributes: footerAttrs) let footerRect = NSRect(x: rect.origin.x, y: rect.origin.y + 10, width: rect.size.width, height: 30) footerString.draw(in: footerRect) } // 定义打印内容区域 override var printFrame: NSRect { return bounds.inset(by: NSEdgeInsets(top: 60, left: 40, bottom: 60, right: 40)) } }
- 修改
HTMLPrintView的打印逻辑,使用自定义视图:
public class HTMLPrintView: NSView { var webView: WKWebView private var documentTitle: String = "" public override init(frame frameRect: NSRect) { webView = WKWebView(frame: frameRect) super.init(frame: frameRect) addSubview(webView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func printView(htmlContent: String, window parentWindow: NSWindow, documentTitle: String) { self.documentTitle = documentTitle let webView = WKWebView(frame: .zero) webView.navigationDelegate = self webView.loadHTMLString(htmlContent, baseURL: nil) } } extension HTMLPrintView: WKNavigationDelegate { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { let printInfo = NSPrintInfo() printInfo.horizontalPagination = .fit printInfo.verticalPagination = .fit // 清空默认边距,由自定义视图控制 printInfo.topMargin = 0 printInfo.bottomMargin = 0 printInfo.leftMargin = 0 printInfo.rightMargin = 0 let printView = CustomPrintView(frame: printInfo.paperRect, webView: webView, title: documentTitle) let printOperation = NSPrintOperation(view: printView, printInfo: printInfo) printOperation.printPanel.options.insert(.showsPaperSize) printOperation.printPanel.options.insert(.showsOrientation) printOperation.printPanel.options.insert(.showsPreview) printOperation.runModal(for: parentWindow, delegate: self, didRun: nil, contextInfo: nil) } }
关键注意事项
- 绝对不要用固定延迟等待页面加载,必须通过
WKNavigationDelegate的didFinish方法确保页面完全渲染后再打印,否则会出现样式失效或内容缺失问题。 - CSS方案中要添加
media="print",确保样式仅在打印场景生效,不影响页面正常显示。 - 自定义打印视图时,要准确设置webView的frame,避免正文内容被页眉页脚遮挡。
内容的提问来源于stack exchange,提问作者sab
相关产品推荐
相关产品推荐

